C/C++ BMP图像处理:R分量直方图均衡
本文将介绍如何使用 C 或 C++ 编程语言对大小为 256×256 的 24 位真彩 BMP 格式的图像进行 R 分量直方图均衡操作,并使用 fread() 和 fwrite() 函数进行文件读写。
由于涉及到 BMP 文件的读写,我们需要先定义一些结构体和函数来读写 BMP 文件。以下是一个简单的 BMP 文件读写函数:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int LONG;
#pragma pack(2)
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件类型,必须为 0x4d42
DWORD bfSize; // 文件大小,以字节为单位
WORD bfReserved1; // 保留字段,必须为 0
WORD bfReserved2; // 保留字段,必须为 0
DWORD bfOffBits; // 从文件头到位图数据的偏移量,以字节为单位
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 信息头大小,以字节为单位
LONG biWidth; // 图像宽度,以像素为单位
LONG biHeight; // 图像高度,以像素为单位
WORD biPlanes; // 位平面数,必须为 1
WORD biBitCount; // 每像素位数,1、4、8、16、24、或 32
DWORD biCompression; // 压缩类型,0 表示不压缩
DWORD biSizeImage; // 压缩图像大小,以字节为单位
LONG biXPelsPerMeter; // 水平分辨率,以像素/米为单位
LONG biYPelsPerMeter; // 垂直分辨率,以像素/米为单位
DWORD biClrUsed; // 实际使用的颜色表中的颜色数
DWORD biClrImportant; // 对图像显示有重要影响的颜色数,0 表示都重要
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 蓝色分量
BYTE rgbGreen; // 绿色分量
BYTE rgbRed; // 红色分量
BYTE rgbReserved; // 保留值
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;
void read_bmp(const char* filename, BYTE** data, int* width, int* height) {
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
printf("Failed to open file: %s\n", filename);
exit(1);
}
BITMAPFILEHEADER file_header;
BITMAPINFO* info_header;
fread(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);
if (file_header.bfType != 0x4d42) {
printf("Invalid BMP file: %s\n", filename);
fclose(fp);
exit(1);
}
info_header = (BITMAPINFO*)malloc(sizeof(BITMAPINFO));
fread(&info_header->bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp);
if (info_header->bmiHeader.biBitCount != 24) {
printf("Invalid BMP file: %s\n", filename);
fclose(fp);
exit(1);
}
int padding = (4 - (info_header->bmiHeader.biWidth * 3) % 4) % 4;
int size = info_header->bmiHeader.biWidth * info_header->bmiHeader.biHeight * 3;
BYTE* buffer = (BYTE*)malloc(size);
for (int i = 0; i < info_header->bmiHeader.biHeight; i++) {
fread(buffer + i * info_header->bmiHeader.biWidth * 3, 3, info_header->bmiHeader.biWidth, fp);
fseek(fp, padding, SEEK_CUR);
}
fclose(fp);
*data = buffer;
*width = info_header->bmiHeader.biWidth;
*height = info_header->bmiHeader.biHeight;
free(info_header);
}
void write_bmp(const char* filename, BYTE* data, int width, int height) {
FILE* fp = fopen(filename, "wb");
if (fp == NULL) {
printf("Failed to open file: %s\n", filename);
exit(1);
}
BITMAPFILEHEADER file_header;
BITMAPINFOHEADER info_header;
int padding = (4 - (width * 3) % 4) % 4;
int size = width * height * 3 + height * padding;
file_header.bfType = 0x4d42;
file_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + size;
file_header.bfReserved1 = 0;
file_header.bfReserved2 = 0;
file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
info_header.biSize = sizeof(BITMAPINFOHEADER);
info_header.biWidth = width;
info_header.biHeight = height;
info_header.biPlanes = 1;
info_header.biBitCount = 24;
info_header.biCompression = 0;
info_header.biSizeImage = size;
info_header.biXPelsPerMeter = 0;
info_header.biYPelsPerMeter = 0;
info_header.biClrUsed = 0;
info_header.biClrImportant = 0;
fwrite(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&info_header, sizeof(BITMAPINFOHEADER), 1, fp);
for (int i = 0; i < height; i++) {
fwrite(data + i * width * 3, 3, width, fp);
for (int j = 0; j < padding; j++) {
fputc(0, fp);
}
}
fclose(fp);
}
接下来就可以对 R 分量进行直方图均衡了。直方图均衡的具体实现可以参考其他资料,这里不再赘述。以下是完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int LONG;
#pragma pack(2)
typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件类型,必须为 0x4d42
DWORD bfSize; // 文件大小,以字节为单位
WORD bfReserved1; // 保留字段,必须为 0
WORD bfReserved2; // 保留字段,必须为 0
DWORD bfOffBits; // 从文件头到位图数据的偏移量,以字节为单位
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 信息头大小,以字节为单位
LONG biWidth; // 图像宽度,以像素为单位
LONG biHeight; // 图像高度,以像素为单位
WORD biPlanes; // 位平面数,必须为 1
WORD biBitCount; // 每像素位数,1、4、8、16、24、或 32
DWORD biCompression; // 压缩类型,0 表示不压缩
DWORD biSizeImage; // 压缩图像大小,以字节为单位
LONG biXPelsPerMeter; // 水平分辨率,以像素/米为单位
LONG biYPelsPerMeter; // 垂直分辨率,以像素/米为单位
DWORD biClrUsed; // 实际使用的颜色表中的颜色数
DWORD biClrImportant; // 对图像显示有重要影响的颜色数,0 表示都重要
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
BYTE rgbBlue; // 蓝色分量
BYTE rgbGreen; // 绿色分量
BYTE rgbRed; // 红色分量
BYTE rgbReserved; // 保留值
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;
void read_bmp(const char* filename, BYTE** data, int* width, int* height) {
FILE* fp = fopen(filename, "rb");
if (fp == NULL) {
printf("Failed to open file: %s\n", filename);
exit(1);
}
BITMAPFILEHEADER file_header;
BITMAPINFO* info_header;
fread(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);
if (file_header.bfType != 0x4d42) {
printf("Invalid BMP file: %s\n", filename);
fclose(fp);
exit(1);
}
info_header = (BITMAPINFO*)malloc(sizeof(BITMAPINFO));
fread(&info_header->bmiHeader, sizeof(BITMAPINFOHEADER), 1, fp);
if (info_header->bmiHeader.biBitCount != 24) {
printf("Invalid BMP file: %s\n", filename);
fclose(fp);
exit(1);
}
int padding = (4 - (info_header->bmiHeader.biWidth * 3) % 4) % 4;
int size = info_header->bmiHeader.biWidth * info_header->bmiHeader.biHeight * 3;
BYTE* buffer = (BYTE*)malloc(size);
for (int i = 0; i < info_header->bmiHeader.biHeight; i++) {
fread(buffer + i * info_header->bmiHeader.biWidth * 3, 3, info_header->bmiHeader.biWidth, fp);
fseek(fp, padding, SEEK_CUR);
}
fclose(fp);
*data = buffer;
*width = info_header->bmiHeader.biWidth;
*height = info_header->bmiHeader.biHeight;
free(info_header);
}
void write_bmp(const char* filename, BYTE* data, int width, int height) {
FILE* fp = fopen(filename, "wb");
if (fp == NULL) {
printf("Failed to open file: %s\n", filename);
exit(1);
}
BITMAPFILEHEADER file_header;
BITMAPINFOHEADER info_header;
int padding = (4 - (width * 3) % 4) % 4;
int size = width * height * 3 + height * padding;
file_header.bfType = 0x4d42;
file_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + size;
file_header.bfReserved1 = 0;
file_header.bfReserved2 = 0;
file_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
info_header.biSize = sizeof(BITMAPINFOHEADER);
info_header.biWidth = width;
info_header.biHeight = height;
info_header.biPlanes = 1;
info_header.biBitCount = 24;
info_header.biCompression = 0;
info_header.biSizeImage = size;
info_header.biXPelsPerMeter = 0;
info_header.biYPelsPerMeter = 0;
info_header.biClrUsed = 0;
info_header.biClrImportant = 0;
fwrite(&file_header, sizeof(BITMAPFILEHEADER), 1, fp);
fwrite(&info_header, sizeof(BITMAPINFOHEADER), 1, fp);
for (int i = 0; i < height; i++) {
fwrite(data + i * width * 3, 3, width, fp);
for (int j = 0; j < padding; j++) {
fputc(0, fp);
}
}
fclose(fp);
}
void histogram_equalization(BYTE* data, int width, int height) {
int histogram[256] = {0};
int cum_histogram[256] = {0};
int max_value = 0;
for (int i = 0; i < width * height; i++) {
histogram[data[i * 3 + 0]]++;
}
for (int i = 0; i < 256; i++) {
if (histogram[i] > max_value) {
max_value = histogram[i];
}
}
for (int i = 0; i < 256; i++) {
cum_histogram[i] = round((double)(255 * (cum_histogram[i - 1] + histogram[i])) / (double)(width * height));
}
for (int i = 0; i < width * height; i++) {
data[i * 3 + 0] = cum_histogram[data[i * 3 + 0]];
}
}
int main() {
BYTE* data;
int width, height;
read_bmp("3.bmp", &data, &width, &height);
histogram_equalization(data, width, height);
write_bmp("4.bmp", data, width, height);
free(data);
return 0;
}
原文地址: https://www.cveoy.top/t/topic/jCVx 著作权归作者所有。请勿转载和采集!