C++ BMP图像处理:直方图均衡化实现
本文介绍如何使用C++编程对大小为256×256的24位真彩BMP格式图像进行相应操作,并使用fread()、fwrite()函数进行文件读写。具体而言,我们将对图像'3.bmp'中的色彩R G B分量中的R分量进行直方图均衡化,并将处理后的图像输出为'4.bmp'。
由于本题需要对BMP格式的图像进行读写和处理,需要了解BMP格式的文件结构。BMP格式的文件由文件头、位图信息头和像素数据三部分组成。其中,文件头包括文件类型、文件大小、保留字、位图数据偏移等信息;位图信息头包括位图宽度、高度、位深度、压缩类型等信息;像素数据则是实际的图像数据。具体的文件结构可以参考BMP文件格式的相关资料。
在读取BMP文件时,需要先读取文件头和位图信息头,再根据位图信息头中的宽度、高度和位深度等信息计算出像素数据的大小,最后读取像素数据。对于24位真彩图像,每个像素由3个字节组成,分别表示R、G、B三个分量,因此需要按照这个顺序读取和处理数据。
对于直方图均衡化的操作,可以按照以下步骤进行:
-
统计图像中每个灰度级的像素个数,得到原始直方图。
-
根据原始直方图计算出累积直方图,即每个灰度级之前的像素个数之和。
-
根据累积直方图和像素总数计算出每个灰度级的均衡化映射函数。
-
对于R分量,将每个像素的R值作为灰度级,根据均衡化映射函数计算出新的R值,并将G、B分量保持不变。
-
将处理后的像素数据写入输出文件。
下面是一份可能的代码实现,仅供参考:
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cmath>
#pragma pack(push, 1)
struct BMPFileHeader {
uint16_t bfType; // 文件类型,必须为0x4D42
uint32_t bfSize; // 文件大小,单位为字节
uint16_t bfReserved1; // 保留字1,必须为0
uint16_t bfReserved2; // 保留字2,必须为0
uint32_t bfOffBits; // 位图数据偏移,单位为字节
};
struct BMPInfoHeader {
uint32_t biSize; // 信息头大小,必须为40
int32_t biWidth; // 图像宽度,单位为像素
int32_t biHeight; // 图像高度,单位为像素
uint16_t biPlanes; // 颜色平面数,必须为1
uint16_t biBitCount; // 位深度,表示每个像素占用的位数
uint32_t biCompression; // 压缩类型,0表示不压缩
uint32_t biSizeImage; // 图像数据大小,单位为字节
int32_t biXPelsPerMeter; // 水平分辨率,单位为像素/米
int32_t biYPelsPerMeter; // 垂直分辨率,单位为像素/米
uint32_t biClrUsed; // 颜色表中的颜色数,0表示使用所有颜色
uint32_t biClrImportant; // 重要颜色数,0表示所有颜色都重要
};
#pragma pack(pop)
// 读取一个16位无符号整数
uint16_t readUInt16(FILE* fp) {
uint16_t value;
fread(&value, sizeof(value), 1, fp);
return value;
}
// 读取一个32位有符号整数
int32_t readInt32(FILE* fp) {
int32_t value;
fread(&value, sizeof(value), 1, fp);
return value;
}
// 读取一个32位无符号整数
uint32_t readUInt32(FILE* fp) {
uint32_t value;
fread(&value, sizeof(value), 1, fp);
return value;
}
// 写入一个16位无符号整数
void writeUInt16(FILE* fp, uint16_t value) {
fwrite(&value, sizeof(value), 1, fp);
}
// 写入一个32位有符号整数
void writeInt32(FILE* fp, int32_t value) {
fwrite(&value, sizeof(value), 1, fp);
}
// 写入一个32位无符号整数
void writeUInt32(FILE* fp, uint32_t value) {
fwrite(&value, sizeof(value), 1, fp);
}
// 直方图均衡化
void histogramEqualization(uint8_t* data, int width, int height) {
// 统计原始直方图
int histogram[256] = { 0 };
for (int i = 0; i < width * height; i++) {
histogram[data[i * 3 + 2]]++;
}
// 计算累积直方图
int cumulative[256] = { 0 };
cumulative[0] = histogram[0];
for (int i = 1; i < 256; i++) {
cumulative[i] = cumulative[i - 1] + histogram[i];
}
// 计算均衡化映射函数
int total = width * height;
uint8_t mapping[256];
for (int i = 0; i < 256; i++) {
mapping[i] = static_cast<uint8_t>(round((cumulative[i] * 255.0 / total)));
}
// 对R分量进行均衡化
for (int i = 0; i < width * height; i++) {
data[i * 3 + 2] = mapping[data[i * 3 + 2]];
}
}
int main() {
// 打开输入文件
FILE* input = fopen('3.bmp', 'rb');
if (!input) {
printf('Failed to open input file.\n');
return 1;
}
// 读取文件头
BMPFileHeader fileHeader;
fileHeader.bfType = readUInt16(input);
fileHeader.bfSize = readUInt32(input);
fileHeader.bfReserved1 = readUInt16(input);
fileHeader.bfReserved2 = readUInt16(input);
fileHeader.bfOffBits = readUInt32(input);
// 读取位图信息头
BMPInfoHeader infoHeader;
infoHeader.biSize = readUInt32(input);
infoHeader.biWidth = readInt32(input);
infoHeader.biHeight = readInt32(input);
infoHeader.biPlanes = readUInt16(input);
infoHeader.biBitCount = readUInt16(input);
infoHeader.biCompression = readUInt32(input);
infoHeader.biSizeImage = readUInt32(input);
infoHeader.biXPelsPerMeter = readInt32(input);
infoHeader.biYPelsPerMeter = readInt32(input);
infoHeader.biClrUsed = readUInt32(input);
infoHeader.biClrImportant = readUInt32(input);
// 检查文件头和位图信息头是否合法
if (fileHeader.bfType != 0x4D42 || infoHeader.biSize != 40 || infoHeader.biPlanes != 1 || infoHeader.biBitCount != 24) {
printf('Invalid input file.\n');
fclose(input);
return 1;
}
// 计算像素数据大小
int width = infoHeader.biWidth;
int height = abs(infoHeader.biHeight);
int dataSize = width * height * 3;
// 读取像素数据
uint8_t* data = new uint8_t[dataSize];
fseek(input, fileHeader.bfOffBits, SEEK_SET);
fread(data, sizeof(uint8_t), dataSize, input);
fclose(input);
// 对像素数据进行直方图均衡化
histogramEqualization(data, width, height);
// 打开输出文件
FILE* output = fopen('4.bmp', 'wb');
if (!output) {
printf('Failed to open output file.\n');
delete[] data;
return 1;
}
// 写入文件头
writeUInt16(output, 0x4D42);
writeUInt32(output, sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + dataSize);
writeUInt16(output, 0);
writeUInt16(output, 0);
writeUInt32(output, sizeof(BMPFileHeader) + sizeof(BMPInfoHeader));
// 写入位图信息头
writeUInt32(output, 40);
writeInt32(output, width);
writeInt32(output, height);
writeUInt16(output, 1);
writeUInt16(output, 24);
writeUInt32(output, 0);
writeUInt32(output, dataSize);
writeInt32(output, 0);
writeInt32(output, 0);
writeUInt32(output, 0);
writeUInt32(output, 0);
// 写入像素数据
fwrite(data, sizeof(uint8_t), dataSize, output);
fclose(output);
delete[] data;
return 0;
}
原文地址: https://www.cveoy.top/t/topic/jCVY 著作权归作者所有。请勿转载和采集!