C语言实现BMP图像直方图均衡化 (G分量)
由于直方图均衡需要对每个像素点的灰度值进行计算和修改,因此需要先将24位真彩图像转换为灰度图像,再对灰度图像进行直方图均衡,最后再将灰度图像转换回24位真彩图像。
以下是实现代码:
#include <stdio.h>
#include <stdlib.h>
#pragma pack(2) // 按2字节对齐
// bmp文件头
typedef struct {
unsigned short bfType; // 文件类型,必须为0x4D42
unsigned int bfSize; // 文件大小
unsigned short bfReserved1; // 保留,必须为0
unsigned short bfReserved2; // 保留,必须为0
unsigned int bfOffBits; // 位图数据的起始位置
} BMPFILEHEADER;
// bmp信息头
typedef struct {
unsigned int biSize; // 信息头大小
int biWidth; // 图像宽度
int biHeight; // 图像高度
unsigned short biPlanes; // 位平面数,必须为1
unsigned short biBitCount; // 每像素位数
unsigned int biCompression; // 压缩类型
unsigned int biSizeImage; // 压缩图像大小字节数
int biXPelsPerMeter; // 水平分辨率
int biYPelsPerMeter; // 垂直分辨率
unsigned int biClrUsed; // 实际使用的调色板索引数
unsigned int biClrImportant; // 对图像显示有重要影响的颜色索引的数目
} BMPINFOHEADER;
// bmp像素点
typedef struct {
unsigned char b; // 蓝色分量
unsigned char g; // 绿色分量
unsigned char r; // 红色分量
} PIXEL;
// 灰度像素点
typedef struct {
unsigned char gray; // 灰度值
} GRAYPIXEL;
// 直方图
typedef struct {
int count[256]; // 每个灰度值的像素点数量
float prob[256]; // 每个灰度值的像素点概率
float cdf[256]; // 每个灰度值的累计概率分布
unsigned char map[256]; // 每个灰度值的映射值
} HISTOGRAM;
void readBmp(const char* filename, BMPFILEHEADER* fileHeader, BMPINFOHEADER* infoHeader, PIXEL** pixels) {
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
printf("读取bmp文件失败:%s\n", filename);
exit(1);
}
// 读取文件头
fread(fileHeader, sizeof(BMPFILEHEADER), 1, fp);
if (fileHeader->bfType != 0x4D42) {
printf("不是bmp文件:%s\n", filename);
exit(1);
}
// 读取信息头
fread(infoHeader, sizeof(BMPINFOHEADER), 1, fp);
if (infoHeader->biBitCount != 24) {
printf("不是24位真彩bmp文件:%s\n", filename);
exit(1);
}
// 读取像素数据
*pixels = (PIXEL*)malloc(infoHeader->biWidth * infoHeader->biHeight * sizeof(PIXEL));
fseek(fp, fileHeader->bfOffBits, SEEK_SET);
fread(*pixels, sizeof(PIXEL), infoHeader->biWidth * infoHeader->biHeight, fp);
fclose(fp);
}
void writeBmp(const char* filename, BMPFILEHEADER fileHeader, BMPINFOHEADER infoHeader, PIXEL* pixels) {
FILE* fp = fopen(filename, "wb");
if (fp == NULL) {
printf("写入bmp文件失败:%s\n", filename);
exit(1);
}
// 写入文件头
fwrite(&fileHeader, sizeof(BMPFILEHEADER), 1, fp);
// 写入信息头
fwrite(&infoHeader, sizeof(BMPINFOHEADER), 1, fp);
// 写入像素数据
fseek(fp, fileHeader.bfOffBits, SEEK_SET);
fwrite(pixels, sizeof(PIXEL), infoHeader.biWidth * infoHeader.biHeight, fp);
fclose(fp);
}
void rgb2gray(PIXEL* pixels, GRAYPIXEL* grayPixels, int width, int height) {
for (int i = 0; i < width * height; i++) {
grayPixels[i].gray = 0.299 * pixels[i].r + 0.587 * pixels[i].g + 0.114 * pixels[i].b;
}
}
void gray2rgb(GRAYPIXEL* grayPixels, PIXEL* pixels, int width, int height) {
for (int i = 0; i < width * height; i++) {
pixels[i].r = grayPixels[i].gray;
pixels[i].g = grayPixels[i].gray;
pixels[i].b = grayPixels[i].gray;
}
}
void calcHistogram(GRAYPIXEL* grayPixels, int width, int height, HISTOGRAM* histogram) {
// 统计每个灰度值的像素点数量
for (int i = 0; i < 256; i++) {
histogram->count[i] = 0;
}
for (int i = 0; i < width * height; i++) {
histogram->count[grayPixels[i].gray]++;
}
// 计算每个灰度值的像素点概率和累计概率分布
for (int i = 0; i < 256; i++) {
histogram->prob[i] = (float)histogram->count[i] / (float)(width * height);
histogram->cdf[i] = (i == 0 ? histogram->prob[i] : histogram->cdf[i - 1] + histogram->prob[i]);
}
// 计算每个灰度值的映射值
for (int i = 0; i < 256; i++) {
histogram->map[i] = (unsigned char)(255.0f * histogram->cdf[i] + 0.5f);
}
}
void equalizeHistogram(GRAYPIXEL* grayPixels, int width, int height, HISTOGRAM* histogram) {
// 映射每个像素点的灰度值
for (int i = 0; i < width * height; i++) {
grayPixels[i].gray = histogram->map[grayPixels[i].gray];
}
}
int main() {
BMPFILEHEADER fileHeader;
BMPINFOHEADER infoHeader;
PIXEL* pixels;
GRAYPIXEL* grayPixels;
HISTOGRAM histogram;
// 读取bmp文件
readBmp("3.bmp", &fileHeader, &infoHeader, &pixels);
// 转换为灰度图像
grayPixels = (GRAYPIXEL*)malloc(infoHeader.biWidth * infoHeader.biHeight * sizeof(GRAYPIXEL));
rgb2gray(pixels, grayPixels, infoHeader.biWidth, infoHeader.biHeight);
// 计算直方图
calcHistogram(grayPixels, infoHeader.biWidth, infoHeader.biHeight, &histogram);
// 直方图均衡
equalizeHistogram(grayPixels, infoHeader.biWidth, infoHeader.biHeight, &histogram);
// 转换回真彩图像
rgb2gray(pixels, grayPixels, infoHeader.biWidth, infoHeader.biHeight);
gray2rgb(grayPixels, pixels, infoHeader.biWidth, infoHeader.biHeight);
// 写入bmp文件
writeBmp("5.bmp", fileHeader, infoHeader, pixels);
// 释放内存
free(pixels);
free(grayPixels);
return 0;
}
原文地址: https://www.cveoy.top/t/topic/jCXX 著作权归作者所有。请勿转载和采集!