C语言实现BMP图像直方图均衡化:完整代码解析与优化

本文提供了一段使用C语言编写的完整代码,用于对256×256大小的24位真彩BMP格式图像进行直方图均衡化处理。代码详细解释了BMP文件格式、直方图均衡算法,并利用fread()fwrite()函数进行高效的读写操作。

1. 算法原理

直方图均衡化是一种图像增强技术,它通过调整图像的灰度级分布来提高图像的对比度。基本思想是将原图像的灰度级分布变换为近似均匀分布,从而使图像的细节更加明显。

2. 代码实现

由于直方图均衡需要对每个像素点进行计算,所以需要先将BMP图像读入内存中,进行操作后再写回文件。以下是实现直方图均衡化的完整代码:

#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; // 信息头大小,必须为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表示所有颜色都重要
} BMPINFOHEADER;

// bmp像素数据
typedef struct {
    unsigned char b; // 蓝色分量
    unsigned char g; // 绿色分量
    unsigned char r; // 红色分量
} BMPPIXEL;

// 直方图均衡
void histogram_equalization(BMPPIXEL *pixels, int width, int height) {
    int i, j, k;
    int hist[256] = {0}; // 像素值直方图
    int cdf[256] = {0}; // 累积分布函数
    int min_cdf = width * height; // 最小累积分布函数
    int max_cdf = 0; // 最大累积分布函数
    int new_val[256]; // 新像素值

    // 计算像素值直方图
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            hist[pixels[i * width + j].r]++;
        }
    }

    // 计算累积分布函数
    for (i = 0; i < 256; i++) {
        if (i == 0) {
            cdf[i] = hist[i];
        } else {
            cdf[i] = cdf[i - 1] + hist[i];
        }
        if (cdf[i] < min_cdf) {
            min_cdf = cdf[i];
        }
        if (cdf[i] > max_cdf) {
            max_cdf = cdf[i];
        }
    }

    // 计算新像素值
    for (i = 0; i < 256; i++) {
        new_val[i] = (cdf[i] - min_cdf) * 255 / (max_cdf - min_cdf);
    }

    // 更新像素值
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            pixels[i * width + j].r = new_val[pixels[i * width + j].r];
            pixels[i * width + j].g = new_val[pixels[i * width + j].g];
            pixels[i * width + j].b = new_val[pixels[i * width + j].b];
        }
    }
}

int main() {
    FILE *fp_in, *fp_out;
    BMPFILEHEADER file_header;
    BMPINFOHEADER info_header;
    BMPPIXEL *pixels;
    int i, j;

    // 打开文件
    if ((fp_in = fopen("2.bmp", "rb")) == NULL) {
        printf("Error: cannot open file!\n");
        return 1;
    }
    if ((fp_out = fopen("3.bmp", "wb")) == NULL) {
        printf("Error: cannot create file!\n");
        fclose(fp_in);
        return 1;
    }

    // 读取文件头和信息头
    fread(&file_header, sizeof(BMPFILEHEADER), 1, fp_in);
    fread(&info_header, sizeof(BMPINFOHEADER), 1, fp_in);

    // 分配像素数据内存
    pixels = (BMPPIXEL*) malloc(info_header.biWidth * info_header.biHeight * sizeof(BMPPIXEL));

    // 读取像素数据
    for (i = 0; i < info_header.biHeight; i++) {
        for (j = 0; j < info_header.biWidth; j++) {
            fread(&pixels[i * info_header.biWidth + j], sizeof(BMPPIXEL), 1, fp_in);
        }
    }

    // 直方图均衡
    histogram_equalization(pixels, info_header.biWidth, info_header.biHeight);

    // 更新文件头和信息头
    info_header.biSizeImage = info_header.biWidth * info_header.biHeight * sizeof(BMPPIXEL);
    file_header.bfSize = file_header.bfOffBits + info_header.biSizeImage;

    // 写入文件头和信息头
    fwrite(&file_header, sizeof(BMPFILEHEADER), 1, fp_out);
    fwrite(&info_header, sizeof(BMPINFOHEADER), 1, fp_out);

    // 写入像素数据
    for (i = 0; i < info_header.biHeight; i++) {
        for (j = 0; j < info_header.biWidth; j++) {
            fwrite(&pixels[i * info_header.biWidth + j], sizeof(BMPPIXEL), 1, fp_out);
        }
    }

    // 释放内存
    free(pixels);

    // 关闭文件
    fclose(fp_in);
    fclose(fp_out);

    return 0;
}

3. 代码解析

  • BMP文件格式: 代码定义了BMP文件头结构体BMPFILEHEADER和信息头结构体BMPINFOHEADER,以及像素数据结构体BMPPIXEL,用于解析BMP文件。
  • 直方图均衡函数: histogram_equalization()函数实现直方图均衡算法,主要步骤包括:
    • 计算像素值直方图;
    • 计算累积分布函数;
    • 计算新的像素值;
    • 更新像素值。
  • 主函数: main()函数负责打开输入文件和输出文件,读取文件头和信息头,分配像素数据内存,调用histogram_equalization()函数进行直方图均衡处理,最后写入处理后的图像数据到输出文件。

4. 注意事项

  • BMP文件头和信息头中的一些字段必须按照一定的格式填写,否则会导致图片无法正常显示。在本代码中,使用了#pragma pack(2)来指定结构体按2字节对齐,这是因为BMP文件头和信息头中有一些字段需要按照2字节对齐,否则会导致文件大小计算错误。
  • BMP图像的像素数据是按照从下到上、从左到右的顺序存储的,所以在读取和写入像素数据时需要注意顺序。

5. 优化建议

  • 为了提高代码效率,可以考虑使用更高效的算法来计算直方图和累积分布函数,例如使用查找表或并行计算。
  • 可以使用内存映射文件来避免将整个图像读入内存,减少内存消耗。
  • 可以使用多线程来加速图像处理过程。

6. 总结

本文提供了使用C语言实现BMP图像直方图均衡化的完整代码,并详细解析了代码逻辑和实现细节。通过对代码的优化,可以提高图像处理效率和性能。

7. 代码使用说明

  • 将代码保存为histogram_equalization.c文件;
  • 编译并运行代码:gcc histogram_equalization.c -o histogram_equalization
  • 将需要处理的BMP图像文件命名为2.bmp,程序会将处理后的图像文件保存为3.bmp

希望本文能够帮助读者理解BMP图像直方图均衡化的实现原理和代码编写方法。

C语言实现BMP图像直方图均衡化:完整代码解析与优化

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

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