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;
}

代码解释

  1. 读取 BMP 文件头和像素数据: 使用 fread() 函数读取 BMP 文件头和像素数据。注意使用 fseek() 函数将文件指针定位到相应位置。
  2. 计算直方图: 遍历像素数据,统计每个红色分量的出现次数,形成直方图。
  3. 计算累积分布函数 (CDF): 对直方图进行累加,得到每个红色分量出现的累计概率。
  4. 计算均衡化后的映射表: 将累积概率乘以 255,得到每个红色分量的均衡化后的值。
  5. 进行直方图均衡化: 使用映射表对每个像素的红色分量进行变换。
  6. 写入均衡化后的 BMP 文件: 使用 fwrite() 函数将均衡化后的像素数据写入新的 BMP 文件。

注意事项

  • 本题中要求使用 fread()fwrite() 函数进行 BMP 文件的读写操作,因此需要注意以下几点:
    • 读取 BMP 文件头和像素数据时,需要使用 fseek() 函数将文件指针定位到相应位置,否则会读取到无效数据。
    • 写入 BMP 文件头和像素数据时,也需要使用 fseek() 函数将文件指针定位到相应位置,否则会覆盖掉文件头信息。
    • 由于 BMP 文件中的像素数据是按行存储的,每行的字节数需要根据图像宽度和每个像素的位数计算得出,因此在读取和写入像素数据时,需要注意字节数的计算。

完整代码已上传至 GitHub:

https://github.com/linyacool/Programming-Exercises/tree/main/C%2B%2B/BMPHistogramEqualization

C++ 实现 BMP 图像直方图均衡化

原文地址: https://www.cveoy.top/t/topic/jCVN 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录