C++ 实现 BMP 图像直方图均衡化
C++ 实现 BMP 图像直方图均衡化
本文将介绍如何使用 C++ 编程语言对 256×256 大小、24 位真彩的 BMP 格式图像进行直方图均衡化。代码使用 fread() 和 fwrite() 函数进行文件读写操作,并详细解释了 BMP 文件格式和直方图均衡化的算法原理。
BMP 文件格式
BMP 文件由文件头、位图信息头和像素数据三部分组成。
- 文件头 (BITMAPFILEHEADER):包含文件类型、文件大小、位图数据的起始位置等信息。
- 位图信息头 (BITMAPINFOHEADER):包含图像宽度、高度、颜色位数、压缩类型、图像数据大小等信息。
- 像素数据: 存储着图像的 RGB 值,每个像素由一个 RGBQUAD 结构体表示。
更多关于 BMP 文件格式的详细信息,可以参考以下链接:
https://blog.csdn.net/u010019717/article/details/53883891
代码实现
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
// 定义 BMP 文件头结构体
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER {
unsigned short bfType; // 文件类型,必须为0x4d42
unsigned int bfSize; // 文件大小,单位为字节
unsigned short bfReserved1; // 保留字段
unsigned short bfReserved2; // 保留字段
unsigned int bfOffBits; // 位图数据的起始位置,单位为字节
} BITMAPFILEHEADER;
#pragma pack(pop)
// 定义 BMP 位图信息头结构体
#pragma pack(push, 1)
typedef struct tagBITMAPINFOHEADER {
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
} BITMAPINFOHEADER;
#pragma pack(pop)
// 定义 RGB 颜色结构体
typedef struct tagRGBQUAD {
unsigned char rgbBlue; // 蓝色分量
unsigned char rgbGreen; // 绿色分量
unsigned char rgbRed; // 红色分量
unsigned char rgbReserved; // 保留字段
} RGBQUAD;
// 读取 BMP 文件头
void readBmpHeader(FILE* fp, BITMAPFILEHEADER& bmpFileHeader, BITMAPINFOHEADER& bmpInfoHeader) {
fread(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
fread(&bmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
}
// 写入 BMP 文件头
void writeBmpHeader(FILE* fp, BITMAPFILEHEADER bmpFileHeader, BITMAPINFOHEADER bmpInfoHeader) {
fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&bmpInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
}
// 读取 BMP 像素数据
void readBmpData(FILE* fp, RGBQUAD** bmpData, int width, int height) {
*bmpData = new RGBQUAD[width * height];
fseek(fp, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), SEEK_SET);
fread(*bmpData, sizeof(RGBQUAD), width * height, fp);
}
// 写入 BMP 像素数据
void writeBmpData(FILE* fp, RGBQUAD* bmpData, int width, int height) {
fseek(fp, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), SEEK_SET);
fwrite(bmpData, sizeof(RGBQUAD), width * height, fp);
}
// 计算 RGB 直方图
void computeHistogram(RGBQUAD* bmpData, int width, int height, int* histogram) {
memset(histogram, 0, sizeof(int) * 256);
for (int i = 0; i < width * height; i++) {
histogram[bmpData[i].rgbRed]++;
}
}
// 计算 RGB 累积分布函数
void computeCDF(int* histogram, int* cdf) {
cdf[0] = histogram[0];
for (int i = 1; i < 256; i++) {
cdf[i] = cdf[i - 1] + histogram[i];
}
}
// 计算 RGB 均衡化后的映射表
void computeEqualizationMap(int* cdf, int totalPixels, unsigned char* equalizationMap) {
for (int i = 0; i < 256; i++) {
equalizationMap[i] = (unsigned char)(cdf[i] * 255.0 / totalPixels + 0.5);
}
}
// 对 RGB 图像进行直方图均衡
void histogramEqualization(RGBQUAD* bmpData, int width, int height) {
// 计算 RGB 直方图
int histogram[256];
computeHistogram(bmpData, width, height, histogram);
// 计算 RGB 累积分布函数
int cdf[256];
computeCDF(histogram, cdf);
// 计算 RGB 均衡化后的映射表
unsigned char equalizationMap[256];
computeEqualizationMap(cdf, width * height, equalizationMap);
// 对 R 分量进行直方图均衡
for (int i = 0; i < width * height; i++) {
bmpData[i].rgbRed = equalizationMap[bmpData[i].rgbRed];
}
}
int main() {
// 读取原始 BMP 文件
FILE* fp = fopen("3.bmp", "rb");
if (fp == NULL) {
cout << "Failed to open file!" << endl;
return 0;
}
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFOHEADER bmpInfoHeader;
readBmpHeader(fp, bmpFileHeader, bmpInfoHeader);
RGBQUAD* bmpData;
readBmpData(fp, &bmpData, bmpInfoHeader.biWidth, bmpInfoHeader.biHeight);
fclose(fp);
// 对 R 分量进行直方图均衡
histogramEqualization(bmpData, bmpInfoHeader.biWidth, bmpInfoHeader.biHeight);
// 写入均衡化后的 BMP 文件
fp = fopen("4.bmp", "wb");
if (fp == NULL) {
cout << "Failed to open file!" << endl;
return 0;
}
writeBmpHeader(fp, bmpFileHeader, bmpInfoHeader);
writeBmpData(fp, bmpData, bmpInfoHeader.biWidth, bmpInfoHeader.biHeight);
fclose(fp);
// 释放内存
delete[] bmpData;
return 0;
}
代码解释
- 读取 BMP 文件头和像素数据: 使用
fread()函数读取 BMP 文件头和像素数据。注意使用fseek()函数将文件指针定位到相应位置。 - 计算直方图: 遍历像素数据,统计每个红色分量的出现次数,形成直方图。
- 计算累积分布函数 (CDF): 对直方图进行累加,得到每个红色分量出现的累计概率。
- 计算均衡化后的映射表: 将累积概率乘以 255,得到每个红色分量的均衡化后的值。
- 进行直方图均衡化: 使用映射表对每个像素的红色分量进行变换。
- 写入均衡化后的 BMP 文件: 使用
fwrite()函数将均衡化后的像素数据写入新的 BMP 文件。
注意事项
- 本题中要求使用
fread()和fwrite()函数进行 BMP 文件的读写操作,因此需要注意以下几点:- 读取 BMP 文件头和像素数据时,需要使用
fseek()函数将文件指针定位到相应位置,否则会读取到无效数据。 - 写入 BMP 文件头和像素数据时,也需要使用
fseek()函数将文件指针定位到相应位置,否则会覆盖掉文件头信息。 - 由于 BMP 文件中的像素数据是按行存储的,每行的字节数需要根据图像宽度和每个像素的位数计算得出,因此在读取和写入像素数据时,需要注意字节数的计算。
- 读取 BMP 文件头和像素数据时,需要使用
完整代码已上传至 GitHub:
https://github.com/linyacool/Programming-Exercises/tree/main/C%2B%2B/BMPHistogramEqualization
原文地址: https://www.cveoy.top/t/topic/jCVN 著作权归作者所有。请勿转载和采集!