本文将介绍如何使用 C 或 C++ 编程语言对大小为 256×256 的 24 位真彩 BMP 格式的图像进行 R 分量直方图均衡操作,并使用 fread()fwrite() 函数进行文件读写。

由于涉及到 BMP 文件的读写,我们需要先定义一些结构体和函数来读写 BMP 文件。以下是一个简单的 BMP 文件读写函数:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int LONG;

#pragma pack(2)
typedef struct tagBITMAPFILEHEADER {
    WORD bfType; // 文件类型,必须为 0x4d42
    DWORD bfSize; // 文件大小,以字节为单位
    WORD bfReserved1; // 保留字段,必须为 0
    WORD bfReserved2; // 保留字段,必须为 0
    DWORD bfOffBits; // 从文件头到位图数据的偏移量,以字节为单位
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize; // 信息头大小,以字节为单位
    LONG biWidth; // 图像宽度,以像素为单位
    LONG biHeight; // 图像高度,以像素为单位
    WORD biPlanes; // 位平面数,必须为 1
    WORD biBitCount; // 每像素位数,1、4、8、16、24、或 32
    DWORD biCompression; // 压缩类型,0 表示不压缩
    DWORD biSizeImage; // 压缩图像大小,以字节为单位
    LONG biXPelsPerMeter; // 水平分辨率,以像素/米为单位
    LONG biYPelsPerMeter; // 垂直分辨率,以像素/米为单位
    DWORD biClrUsed; // 实际使用的颜色表中的颜色数
    DWORD biClrImportant; // 对图像显示有重要影响的颜色数,0 表示都重要
} BITMAPINFOHEADER;

typedef struct tagRGBQUAD {
    BYTE rgbBlue; // 蓝色分量
    BYTE rgbGreen; // 绿色分量
    BYTE rgbRed; // 红色分量
    BYTE rgbReserved; // 保留值
} RGBQUAD;

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER bmiHeader; // 位图信息头
    RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;

void read_bmp(const char* filename, BYTE** data, int* width, int* height) {
    FILE* fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("Failed to open file: %s\n", filename);
        exit(1);
    }

    BITMAPFILEHEADER file_header;
    BITMAPINFO* info_header;

    fread(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);

    if (file_header.bfType != 0x4d42) {
        printf("Invalid BMP file: %s\n", filename);
        fclose(fp);
        exit(1);
    }

    info_header = (BITMAPINFO*)malloc(sizeof(BITMAPINFO));
    fread(&info_header->bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp);

    if (info_header->bmiHeader.biBitCount != 24) {
        printf("Invalid BMP file: %s\n", filename);
        fclose(fp);
        exit(1);
    }

    int padding = (4 - (info_header->bmiHeader.biWidth * 3) % 4) % 4;
    int size = info_header->bmiHeader.biWidth * info_header->bmiHeader.biHeight * 3;
    BYTE* buffer = (BYTE*)malloc(size);

    for (int i = 0; i < info_header->bmiHeader.biHeight; i++) {
        fread(buffer + i * info_header->bmiHeader.biWidth * 3, 3, info_header->bmiHeader.biWidth, fp);
        fseek(fp, padding, SEEK_CUR);
    }

    fclose(fp);

    *data = buffer;
    *width = info_header->bmiHeader.biWidth;
    *height = info_header->bmiHeader.biHeight;

    free(info_header);
}

void write_bmp(const char* filename, BYTE* data, int width, int height) {
    FILE* fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("Failed to open file: %s\n", filename);
        exit(1);
    }

    BITMAPFILEHEADER file_header;
    BITMAPINFOHEADER info_header;

    int padding = (4 - (width * 3) % 4) % 4;
    int size = width * height * 3 + height * padding;

    file_header.bfType = 0x4d42;
    file_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + size;
    file_header.bfReserved1 = 0;
    file_header.bfReserved2 = 0;
    file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    info_header.biSize = sizeof(BITMAPINFOHEADER);
    info_header.biWidth = width;
    info_header.biHeight = height;
    info_header.biPlanes = 1;
    info_header.biBitCount = 24;
    info_header.biCompression = 0;
    info_header.biSizeImage = size;
    info_header.biXPelsPerMeter = 0;
    info_header.biYPelsPerMeter = 0;
    info_header.biClrUsed = 0;
    info_header.biClrImportant = 0;

    fwrite(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);
    fwrite(&info_header, sizeof(BITMAPINFOHEADER), 1, fp);

    for (int i = 0; i < height; i++) {
        fwrite(data + i * width * 3, 3, width, fp);
        for (int j = 0; j < padding; j++) {
            fputc(0, fp);
        }
    }

    fclose(fp);
}

接下来就可以对 R 分量进行直方图均衡了。直方图均衡的具体实现可以参考其他资料,这里不再赘述。以下是完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int LONG;

#pragma pack(2)
typedef struct tagBITMAPFILEHEADER {
    WORD bfType; // 文件类型,必须为 0x4d42
    DWORD bfSize; // 文件大小,以字节为单位
    WORD bfReserved1; // 保留字段,必须为 0
    WORD bfReserved2; // 保留字段,必须为 0
    DWORD bfOffBits; // 从文件头到位图数据的偏移量,以字节为单位
} BITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize; // 信息头大小,以字节为单位
    LONG biWidth; // 图像宽度,以像素为单位
    LONG biHeight; // 图像高度,以像素为单位
    WORD biPlanes; // 位平面数,必须为 1
    WORD biBitCount; // 每像素位数,1、4、8、16、24、或 32
    DWORD biCompression; // 压缩类型,0 表示不压缩
    DWORD biSizeImage; // 压缩图像大小,以字节为单位
    LONG biXPelsPerMeter; // 水平分辨率,以像素/米为单位
    LONG biYPelsPerMeter; // 垂直分辨率,以像素/米为单位
    DWORD biClrUsed; // 实际使用的颜色表中的颜色数
    DWORD biClrImportant; // 对图像显示有重要影响的颜色数,0 表示都重要
} BITMAPINFOHEADER;

typedef struct tagRGBQUAD {
    BYTE rgbBlue; // 蓝色分量
    BYTE rgbGreen; // 绿色分量
    BYTE rgbRed; // 红色分量
    BYTE rgbReserved; // 保留值
} RGBQUAD;

typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER bmiHeader; // 位图信息头
    RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;

void read_bmp(const char* filename, BYTE** data, int* width, int* height) {
    FILE* fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("Failed to open file: %s\n", filename);
        exit(1);
    }

    BITMAPFILEHEADER file_header;
    BITMAPINFO* info_header;

    fread(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);

    if (file_header.bfType != 0x4d42) {
        printf("Invalid BMP file: %s\n", filename);
        fclose(fp);
        exit(1);
    }

    info_header = (BITMAPINFO*)malloc(sizeof(BITMAPINFO));
    fread(&info_header->bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp);

    if (info_header->bmiHeader.biBitCount != 24) {
        printf("Invalid BMP file: %s\n", filename);
        fclose(fp);
        exit(1);
    }

    int padding = (4 - (info_header->bmiHeader.biWidth * 3) % 4) % 4;
    int size = info_header->bmiHeader.biWidth * info_header->bmiHeader.biHeight * 3;
    BYTE* buffer = (BYTE*)malloc(size);

    for (int i = 0; i < info_header->bmiHeader.biHeight; i++) {
        fread(buffer + i * info_header->bmiHeader.biWidth * 3, 3, info_header->bmiHeader.biWidth, fp);
        fseek(fp, padding, SEEK_CUR);
    }

    fclose(fp);

    *data = buffer;
    *width = info_header->bmiHeader.biWidth;
    *height = info_header->bmiHeader.biHeight;

    free(info_header);
}

void write_bmp(const char* filename, BYTE* data, int width, int height) {
    FILE* fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("Failed to open file: %s\n", filename);
        exit(1);
    }

    BITMAPFILEHEADER file_header;
    BITMAPINFOHEADER info_header;

    int padding = (4 - (width * 3) % 4) % 4;
    int size = width * height * 3 + height * padding;

    file_header.bfType = 0x4d42;
    file_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + size;
    file_header.bfReserved1 = 0;
    file_header.bfReserved2 = 0;
    file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    info_header.biSize = sizeof(BITMAPINFOHEADER);
    info_header.biWidth = width;
    info_header.biHeight = height;
    info_header.biPlanes = 1;
    info_header.biBitCount = 24;
    info_header.biCompression = 0;
    info_header.biSizeImage = size;
    info_header.biXPelsPerMeter = 0;
    info_header.biYPelsPerMeter = 0;
    info_header.biClrUsed = 0;
    info_header.biClrImportant = 0;

    fwrite(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);
    fwrite(&info_header, sizeof(BITMAPINFOHEADER), 1, fp);

    for (int i = 0; i < height; i++) {
        fwrite(data + i * width * 3, 3, width, fp);
        for (int j = 0; j < padding; j++) {
            fputc(0, fp);
        }
    }

    fclose(fp);
}

void histogram_equalization(BYTE* data, int width, int height) {
    int histogram[256] = {0};
    int cum_histogram[256] = {0};
    int max_value = 0;

    for (int i = 0; i < width * height; i++) {
        histogram[data[i * 3 + 0]]++;
    }

    for (int i = 0; i < 256; i++) {
        if (histogram[i] > max_value) {
            max_value = histogram[i];
        }
    }

    for (int i = 0; i < 256; i++) {
        cum_histogram[i] = round((double)(255 * (cum_histogram[i - 1] + histogram[i])) / (double)(width * height));
    }

    for (int i = 0; i < width * height; i++) {
        data[i * 3 + 0] = cum_histogram[data[i * 3 + 0]];
    }
}

int main() {
    BYTE* data;
    int width, height;

    read_bmp("3.bmp", &data, &width, &height);
    histogram_equalization(data, width, height);
    write_bmp("4.bmp", data, width, height);
    free(data);

    return 0;
}
C/C++ BMP图像处理:R分量直方图均衡

原文地址: https://www.cveoy.top/t/topic/jCVx 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录