C语言实现BMP图像直方图均衡 - RGB分量处理

本代码示例演示了如何使用C语言对256x256大小的24位真彩BMP图像进行直方图均衡操作。代码利用fread()fwrite()函数读取和写入图像数据,并分别对R和G分量进行直方图均衡,最终将处理后的图像保存为5.bmp。

代码如下:

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

// 定义BMP文件头结构体
typedef struct {
    unsigned short bfType;          // 文件类型,必须为0x4D42
    unsigned int bfSize;            // 文件大小,单位为字节
    unsigned short bfReserved1;     // 保留字段
    unsigned short bfReserved2;     // 保留字段
    unsigned int bfOffBits;         // 数据偏移量,单位为字节
} BMPFILEHEADER;

// 定义BMP信息头结构体
typedef struct {
    unsigned int biSize;            // 信息头大小,必须为40
    int biWidth;                    // 图像宽度,单位为像素
    int biHeight;                   // 图像高度,单位为像素
    unsigned short biPlanes;        // 位平面数,必须为1
    unsigned short biBitCount;      // 每个像素的位数,必须为24
    unsigned int biCompression;     // 压缩类型,必须为0(不压缩)
    unsigned int biSizeImage;       // 数据大小,单位为字节
    int biXPelsPerMeter;            // 水平分辨率,单位为像素/米
    int biYPelsPerMeter;            // 垂直分辨率,单位为像素/米
    unsigned int biClrUsed;         // 使用的颜色数,为0表示使用所有颜色
    unsigned int biClrImportant;    // 重要的颜色数,为0表示所有颜色都重要
} BMPINFOHEADER;

// 定义像素结构体
typedef struct {
    unsigned char b, g, r;  // 注意RGB分量的顺序是BGR
} PIXEL;

// 读取BMP文件头
void readBmpFileHeader(FILE* fp, BMPFILEHEADER* fileHeader) {
    fread(&fileHeader->bfType, sizeof(fileHeader->bfType), 1, fp);
    fread(&fileHeader->bfSize, sizeof(fileHeader->bfSize), 1, fp);
    fread(&fileHeader->bfReserved1, sizeof(fileHeader->bfReserved1), 1, fp);
    fread(&fileHeader->bfReserved2, sizeof(fileHeader->bfReserved2), 1, fp);
    fread(&fileHeader->bfOffBits, sizeof(fileHeader->bfOffBits), 1, fp);
}

// 读取BMP信息头
void readBmpInfoHeader(FILE* fp, BMPINFOHEADER* infoHeader) {
    fread(&infoHeader->biSize, sizeof(infoHeader->biSize), 1, fp);
    fread(&infoHeader->biWidth, sizeof(infoHeader->biWidth), 1, fp);
    fread(&infoHeader->biHeight, sizeof(infoHeader->biHeight), 1, fp);
    fread(&infoHeader->biPlanes, sizeof(infoHeader->biPlanes), 1, fp);
    fread(&infoHeader->biBitCount, sizeof(infoHeader->biBitCount), 1, fp);
    fread(&infoHeader->biCompression, sizeof(infoHeader->biCompression), 1, fp);
    fread(&infoHeader->biSizeImage, sizeof(infoHeader->biSizeImage), 1, fp);
    fread(&infoHeader->biXPelsPerMeter, sizeof(infoHeader->biXPelsPerMeter), 1, fp);
    fread(&infoHeader->biYPelsPerMeter, sizeof(infoHeader->biYPelsPerMeter), 1, fp);
    fread(&infoHeader->biClrUsed, sizeof(infoHeader->biClrUsed), 1, fp);
    fread(&infoHeader->biClrImportant, sizeof(infoHeader->biClrImportant), 1, fp);
}

// 读取像素数组
void readPixels(FILE* fp, PIXEL* pixels, int width, int height) {
    int padding = (4 - ((width * 3) % 4)) % 4;  // 计算每行的填充字节数
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            fread(&pixels[i * width + j], sizeof(PIXEL), 1, fp);
        }
        fseek(fp, padding, SEEK_CUR);  // 跳过填充字节
    }
}

// 写入BMP文件头
void writeBmpFileHeader(FILE* fp, BMPFILEHEADER* fileHeader) {
    fwrite(&fileHeader->bfType, sizeof(fileHeader->bfType), 1, fp);
    fwrite(&fileHeader->bfSize, sizeof(fileHeader->bfSize), 1, fp);
    fwrite(&fileHeader->bfReserved1, sizeof(fileHeader->bfReserved1), 1, fp);
    fwrite(&fileHeader->bfReserved2, sizeof(fileHeader->bfReserved2), 1, fp);
    fwrite(&fileHeader->bfOffBits, sizeof(fileHeader->bfOffBits), 1, fp);
}

// 写入BMP信息头
void writeBmpInfoHeader(FILE* fp, BMPINFOHEADER* infoHeader) {
    fwrite(&infoHeader->biSize, sizeof(infoHeader->biSize), 1, fp);
    fwrite(&infoHeader->biWidth, sizeof(infoHeader->biWidth), 1, fp);
    fwrite(&infoHeader->biHeight, sizeof(infoHeader->biHeight), 1, fp);
    fwrite(&infoHeader->biPlanes, sizeof(infoHeader->biPlanes), 1, fp);
    fwrite(&infoHeader->biBitCount, sizeof(infoHeader->biBitCount), 1, fp);
    fwrite(&infoHeader->biCompression, sizeof(infoHeader->biCompression), 1, fp);
    fwrite(&infoHeader->biSizeImage, sizeof(infoHeader->biSizeImage), 1, fp);
    fwrite(&infoHeader->biXPelsPerMeter, sizeof(infoHeader->biXPelsPerMeter), 1, fp);
    fwrite(&infoHeader->biYPelsPerMeter, sizeof(infoHeader->biYPelsPerMeter), 1, fp);
    fwrite(&infoHeader->biClrUsed, sizeof(infoHeader->biClrUsed), 1, fp);
    fwrite(&infoHeader->biClrImportant, sizeof(infoHeader->biClrImportant), 1, fp);
}

// 写入像素数组
void writePixels(FILE* fp, PIXEL* pixels, int width, int height) {
    int padding = (4 - ((width * 3) % 4)) % 4;  // 计算每行的填充字节数
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            fwrite(&pixels[i * width + j], sizeof(PIXEL), 1, fp);
        }
        for (int j = 0; j < padding; j++) {
            fputc(0, fp);  // 写入填充字节
        }
    }
}

// 计算直方图
void calcHistogram(PIXEL* pixels, int width, int height, int* histR, int* histG, int* histB) {
    for (int i = 0; i < 256; i++) {
        histR[i] = 0;
        histG[i] = 0;
        histB[i] = 0;
    }
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            histR[pixels[i * width + j].r]++;
            histG[pixels[i * width + j].g]++;
            histB[pixels[i * width + j].b]++;
        }
    }
}

// 计算累积直方图
void calcCumulativeHistogram(int* hist, int* cumHist) {
    cumHist[0] = hist[0];
    for (int i = 1; i < 256; i++) {
        cumHist[i] = cumHist[i - 1] + hist[i];
    }
}

// 直方图均衡
void histogramEqualization(PIXEL* pixels, int width, int height, int* hist, int* cumHist) {
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            int r = pixels[i * width + j].r;
            pixels[i * width + j].r = 255.0 * cumHist[r] / (width * height) + 0.5;
        }
    }
}

int main() {
    FILE* fpIn = fopen("3.bmp", "rb");
    FILE* fpOut = fopen("5.bmp", "wb");
    if (fpIn == NULL || fpOut == NULL) {
        printf("文件打开失败\n");
        return 0;
    }

    BMPFILEHEADER fileHeader;
    BMPINFOHEADER infoHeader;
    PIXEL* pixels;

    // 读取BMP文件头和信息头
    readBmpFileHeader(fpIn, &fileHeader);
    readBmpInfoHeader(fpIn, &infoHeader);

    // 分配像素数组内存
    pixels = (PIXEL*)malloc(infoHeader.biWidth * infoHeader.biHeight * sizeof(PIXEL));

    // 读取像素数组
    readPixels(fpIn, pixels, infoHeader.biWidth, infoHeader.biHeight);

    // 计算直方图
    int histR[256], histG[256], histB[256];
    calcHistogram(pixels, infoHeader.biWidth, infoHeader.biHeight, histR, histG, histB);

    // 计算累积直方图
    int cumHistR[256], cumHistG[256], cumHistB[256];
    calcCumulativeHistogram(histR, cumHistR);
    calcCumulativeHistogram(histG, cumHistG);
    calcCumulativeHistogram(histB, cumHistB);

    // 直方图均衡
    histogramEqualization(pixels, infoHeader.biWidth, infoHeader.biHeight, histR, cumHistR); // 对R分量进行直方图均衡
    histogramEqualization(pixels, infoHeader.biWidth, infoHeader.biHeight, histG, cumHistG); // 对G分量进行直方图均衡

    // 写入BMP文件头和信息头
    writeBmpFileHeader(fpOut, &fileHeader);
    writeBmpInfoHeader(fpOut, &infoHeader);

    // 写入像素数组
    writePixels(fpOut, pixels, infoHeader.biWidth, infoHeader.biHeight);

    // 释放内存
    free(pixels);

    // 关闭文件
    fclose(fpIn);
    fclose(fpOut);

    return 0;
}

注意:

  • BMP图像中每个像素的RGB分量的顺序是BGR,而不是RGB。因此在读取和写入像素数组时,要注意分别读取和写入B、G、R三个分量。
  • 代码中只对R和G分量进行了直方图均衡,如果需要对B分量进行处理,可以添加类似的代码。

使用方法:

  1. 将代码保存为一个名为histogram_equalization.c的文件。
  2. 编译代码:gcc histogram_equalization.c -o histogram_equalization
  3. 运行代码:./histogram_equalization
  4. 代码会读取名为3.bmp的图像文件,并将其处理后的结果保存到名为5.bmp的图像文件。

运行结果:

运行代码后,将会生成一个名为5.bmp的图像文件,该图像文件是3.bmp图像经过直方图均衡处理后的结果。

其他说明:

  • 代码中的readPixels()writePixels()函数使用了fseek()函数来处理BMP图像中每行数据填充字节的问题。
  • 代码中使用了malloc()函数来动态分配内存,并在程序结束后使用free()函数释放内存。
  • 代码中使用了fopen()函数打开文件,并在程序结束后使用fclose()函数关闭文件。
  • 代码中使用了printf()函数输出信息,方便调试。

希望这份代码示例能够帮助您理解如何使用C语言实现BMP图像直方图均衡。

C语言实现BMP图像直方图均衡 - RGB分量处理

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

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