C语言实现BMP图像直方图均衡 - RGB分量处理
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分量进行处理,可以添加类似的代码。
使用方法:
- 将代码保存为一个名为
histogram_equalization.c的文件。 - 编译代码:
gcc histogram_equalization.c -o histogram_equalization - 运行代码:
./histogram_equalization - 代码会读取名为
3.bmp的图像文件,并将其处理后的结果保存到名为5.bmp的图像文件。
运行结果:
运行代码后,将会生成一个名为5.bmp的图像文件,该图像文件是3.bmp图像经过直方图均衡处理后的结果。
其他说明:
- 代码中的
readPixels()和writePixels()函数使用了fseek()函数来处理BMP图像中每行数据填充字节的问题。 - 代码中使用了
malloc()函数来动态分配内存,并在程序结束后使用free()函数释放内存。 - 代码中使用了
fopen()函数打开文件,并在程序结束后使用fclose()函数关闭文件。 - 代码中使用了
printf()函数输出信息,方便调试。
希望这份代码示例能够帮助您理解如何使用C语言实现BMP图像直方图均衡。
原文地址: https://www.cveoy.top/t/topic/jCXx 著作权归作者所有。请勿转载和采集!