C语言实现BMP图像直方图均衡化 - 将 3.bmp 的 R 分量进行均衡并输出为 4.bmp
由于本题需要对 BMP 格式的图像进行操作,需要先了解 BMP 格式的文件结构。BMP 格式的文件由文件头(14 字节)、位图信息头(40 字节)和像素数据组成,其中位图信息头中包含了图像的宽、高、色深等信息,像素数据则按照一定的格式存储了每个像素的 RGB 值。
在进行直方图均衡操作时,需要先读取图像的像素数据,统计每个像素 R 分量的出现次数,计算出每个 R 分量的累计分布函数,并根据累计分布函数对每个像素的 R 分量进行映射,最后将处理后的像素数据写回文件中。
下面是一份基于 C 语言的代码实现:
#include <stdio.h>
#include <stdlib.h>
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int LONG;
#pragma pack(2) // 按照 2 字节对齐
// BMP 文件头
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件类型,必须为 BM
DWORD bfSize; // 文件大小,单位为字节
WORD bfReserved1; // 保留字,必须为 0
WORD bfReserved2; // 保留字,必须为 0
DWORD bfOffBits; // 从文件头到位图数据的偏移量,单位为字节
} BITMAPFILEHEADER;
// BMP 位图信息头
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 信息头大小,必须为 40
LONG biWidth; // 图像宽度,单位为像素
LONG biHeight; // 图像高度,单位为像素
WORD biPlanes; // 颜色平面数,必须为 1
WORD biBitCount; // 每个像素的位数,一般为 24
DWORD biCompression; // 压缩类型,一般为 0
DWORD biSizeImage; // 图像数据大小,单位为字节
LONG biXPelsPerMeter; // 水平分辨率,单位为像素/米
LONG biYPelsPerMeter; // 垂直分辨率,单位为像素/米
DWORD biClrUsed; // 颜色表中实际使用的颜色数,一般为 0
DWORD biClrImportant; // 重要颜色数,一般为 0
} BITMAPINFOHEADER;
#pragma pack() // 恢复默认对齐方式
int main() {
FILE *fp_in, *fp_out;
BITMAPFILEHEADER file_header;
BITMAPINFOHEADER info_header;
BYTE *pixel_data, *new_pixel_data;
int width, height, row_bytes, padding_bytes, i, j, r, g, b;
int *histogram, *cdf, cdf_min, cdf_max;
float cdf_scale;
// 打开输入文件
fp_in = fopen('3.bmp', 'rb');
if (fp_in == NULL) {
printf('Failed to open input file.\n');
return 1;
}
// 读取 BMP 文件头
fread(&file_header, sizeof(BITMAPFILEHEADER), 1, fp_in);
if (file_header.bfType != 0x4d42) {
printf('Invalid BMP file.\n');
return 1;
}
// 读取 BMP 位图信息头
fread(&info_header, sizeof(BITMAPINFOHEADER), 1, fp_in);
if (info_header.biBitCount != 24) {
printf('Unsupported color depth.\n');
return 1;
}
// 计算图像宽度、高度、每行字节数和填充字节数
width = info_header.biWidth;
height = info_header.biHeight;
row_bytes = ((width * 3) + 3) / 4 * 4;
padding_bytes = row_bytes - width * 3;
// 分配像素数据和新的像素数据
pixel_data = (BYTE*)malloc(row_bytes * height);
new_pixel_data = (BYTE*)malloc(row_bytes * height);
// 读取像素数据
fread(pixel_data, 1, row_bytes * height, fp_in);
// 关闭输入文件
fclose(fp_in);
// 计算 R 分量的直方图
histogram = (int*)calloc(256, sizeof(int));
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
b = *(pixel_data + i * row_bytes + j * 3);
g = *(pixel_data + i * row_bytes + j * 3 + 1);
r = *(pixel_data + i * row_bytes + j * 3 + 2);
histogram[r]++;
}
}
// 计算 R 分量的累计分布函数
cdf = (int*)calloc(256, sizeof(int));
cdf[0] = histogram[0];
for (i = 1; i < 256; i++) {
cdf[i] = cdf[i - 1] + histogram[i];
}
// 寻找最小和最大的累计分布函数值
cdf_min = cdf[0];
cdf_max = cdf[255];
for (i = 0; i < 256; i++) {
if (cdf[i] < cdf_min) {
cdf_min = cdf[i];
}
if (cdf[i] > cdf_max) {
cdf_max = cdf[i];
}
}
// 计算累计分布函数的缩放比例
cdf_scale = 255.0 / (cdf_max - cdf_min);
// 对 R 分量进行直方图均衡
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
b = *(pixel_data + i * row_bytes + j * 3);
g = *(pixel_data + i * row_bytes + j * 3 + 1);
r = *(pixel_data + i * row_bytes + j * 3 + 2);
r = (int)((cdf[r] - cdf_min) * cdf_scale + 0.5);
if (r < 0) {
r = 0;
}
if (r > 255) {
r = 255;
}
*(new_pixel_data + i * row_bytes + j * 3) = b;
*(new_pixel_data + i * row_bytes + j * 3 + 1) = g;
*(new_pixel_data + i * row_bytes + j * 3 + 2) = r;
}
}
// 打开输出文件
fp_out = fopen('4.bmp', 'wb');
if (fp_out == NULL) {
printf('Failed to open output file.\n');
return 1;
}
// 写入 BMP 文件头
fwrite(&file_header, sizeof(BITMAPFILEHEADER), 1, fp_out);
// 写入 BMP 位图信息头
fwrite(&info_header, sizeof(BITMAPINFOHEADER), 1, fp_out);
// 写入像素数据
fwrite(new_pixel_data, 1, row_bytes * height, fp_out);
// 关闭输出文件
fclose(fp_out);
// 释放内存
free(pixel_data);
free(new_pixel_data);
free(histogram);
free(cdf);
return 0;
}
需要注意的是,在读取和写入文件时,需要使用二进制模式打开文件(即参数为 'rb' 和 'wb'),否则可能会出现文件格式错误的问题。同时,在读取像素数据时,每行的字节数需要按照 4 字节对齐,因此需要计算每行的填充字节数。在对 R 分量进行直方图均衡时,需要注意对像素值的范围进行限制,否则可能会出现超出范围的像素值。
原文地址: https://www.cveoy.top/t/topic/jCW1 著作权归作者所有。请勿转载和采集!