由于直方图均衡需要对每个像素点的灰度值进行计算和修改,因此需要先将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;
}
C语言实现BMP图像直方图均衡化 (G分量)

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

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