BMP图像直方图均衡化算法程序为什么出来的图片无法显示呀

BMP图像直方图均衡化算法程序为什么出来的图片无法显示呀


#include 
#include 
#include 
#include 
typedef unsigned char BYTE;  // 定义BYTE为一个字节的类型
typedef unsigned short WORD; // 定义WORD为两个字节的类型
typedef unsigned int DWORD;  // 定义DWORD为四个字节的类型
typedef struct PerPixelgbr
{
    BYTE b;
    BYTE g;
    BYTE r;
}BGR;
//14 Bytes 文件头信息块
typedef struct tBITMAPFILEHEADER
{
    DWORD    bfSize;            //图片大小
    DWORD        bfReserved;    //保留字;    
    DWORD    bfOffBits;        //文件头到像素数据偏移量
}TBH;
//40 Bytes 图像描述信息块
typedef struct tBITMAPINFOHEADER
{
    DWORD    biSize;    //此结构体大小,换成kb除以1024
    DWORD    biWidth;            //宽          
    DWORD    biHeight;            //高
    WORD        biPlanes;        //平面显示属,一般显示器只有一个平面所以为1
    WORD        biBitCount;        //一个像素所占的位数,一般为24,带A通道的有32
    DWORD    biCompression;    //图像数据压缩的类,0为不压缩
    DWORD    biSizeImage;    //像素数据所占的大小 =bfSize-bfOffBits
    DWORD    biXPelsPerMeter;//水平分辨率
    DWORD    biYPelsPerMeter;//垂直分辨率
    DWORD    biClrUsed;        //位图实际使用的彩表的彩色索引数,为0证明全部使用
    DWORD    biClrImportant;    //说明对图象显示有重要影响的颜色索引数,如果是0,表示无差别
}TBI;
int main()
{
    FILE* in;
    FILE* out;
    TBH* BITMAPFILEHEADER;
    TBI* BITMAPINFOHEADER;
    BGR* duqu;
    BITMAPFILEHEADER = (TBH*)malloc(sizeof(TBH));
    BITMAPINFOHEADER = (TBI*)malloc(sizeof(TBI));
    duqu = (BGR*)malloc(sizeof(BGR));
    fopen_s(&in, "E:\\121.bmp", "rb");
    WORD  fileType;
    fread(&fileType, sizeof(unsigned short), 1, in);
    if(fileType == 0x4d42)
    {
        printf("文件类型标识正确\n");
    }//判断是不是bmp文件
    else{}
    fread(BITMAPFILEHEADER, sizeof(TBH), 1, in);//读取 14 Bytes 文件头信息块
    fread(BITMAPINFOHEADER, sizeof(TBI), 1, in);//读取 40 Bytes 图像描述信息块
    int W, H;
    W = (BITMAPINFOHEADER->biWidth);//得到宽度
    H = (BITMAPINFOHEADER->biHeight);//得到长度
    uint32_t B_ZFT[256] = { 0 }, G_ZFT[256] = { 0 }, R_ZFT[256] = { 0 };
    uint32_t B_CS[256], G_CS[256], R_CS[256];
    double B_GL[256], G_GL[256], R_GL[256];
    double gailvhe = 0;
    //fseek(in, 18, SEEK_SET);
    //fread(&W, 4, 1, in);
    //fseek(in, 22, SEEK_SET);
    //fread(&H, 4, 1, in);
    //fseek(in, 54, SEEK_SET);
    for (int i = W * H; i > 0; i--)
    {
        fread(duqu,  sizeof(BGR), 1, in);
        B_ZFT[(duqu->b)]++;
        G_ZFT[(duqu->g)]++;
        R_ZFT[(duqu->r)]++;
    }
    free(duqu);
    for (int i = 0; i < 256; i++)
    {
        gailvhe += (double)B_ZFT[i] / ((double)W * (double)H);
        B_GL[i] = gailvhe;
    }
    printf("%lf", gailvhe);
    gailvhe = 0;
    for (int i = 0; i < 256; i++)
    {
        gailvhe += (double)G_ZFT[i] / ((double)W * (double)H);
        G_GL[i] = gailvhe;
    }
    printf("%lf", gailvhe);
    gailvhe = 0;
    for (int i = 0; i < 256; i++)
    {
        gailvhe += (double)R_ZFT[i] / ((double)W * (double)H);
        R_GL[i] = gailvhe;
    }
    printf("%lf", gailvhe);
    for (int i = 0; i < 256; i++)
    {
        B_CS[i] = (uint32_t)(B_GL[i] * 255);
        G_CS[i] = (uint32_t)(G_GL[i] * 255);
        R_CS[i] = (uint32_t)(R_GL[i] * 255);
    }
    BGR* xielu = (BGR*)malloc(sizeof(BGR));
    duqu = (BGR*)malloc(sizeof(BGR));
    fseek(in, 54, SEEK_SET);
    fopen_s(&out, "E:\\12121.bmp", "wb");
    fwrite(&fileType, 2, 1, out);
    fwrite(&BITMAPFILEHEADER, sizeof(TBH), 1, out);
    fwrite(&BITMAPINFOHEADER, sizeof(TBI), 1, out);
    //fseek(out, 54, SEEK_SET);
    for (int i = W * H; i > 0; i--)
    {
        fread(duqu, sizeof(BGR), 1, in);
        (xielu->b) = B_CS[(duqu->b)];
        (xielu->g) = G_CS[(duqu->g)];
        (xielu->r) = R_CS[(duqu->r)];
        fwrite(&xielu, sizeof(BGR), 1, out);
    }
    free(duqu);
    free(xielu);
    fclose(in);
    fclose(out);
    return 0;
}

该回答引用ChatGPT

您的程序存在一些问题,导致输出的 BMP 文件无法正确显示。以下是我发现的几个问题:

1、文件头信息块(BITMAPFILEHEADER)和图像描述信息块(BITMAPINFOHEADER)中的成员应该按照 BMP 文件格式的要求进行对齐。对于结构体成员变量,编译器会默认进行字节对齐,但是结构体中不同成员之间的空隙大小需要手动计算。具体而言,BITMAPFILEHEADER 和 BITMAPINFOHEADER 结构体中的成员变量应该按照如下方式进行定义:

typedef struct tBITMAPFILEHEADER
{
    WORD bfType; // 文件类型,应该为 0x4d42
    DWORD bfSize; // BMP 文件大小,单位是字节
    WORD bfReserved1; // 保留,必须设置为 0
    WORD bfReserved2; // 保留,必须设置为 0
    DWORD bfOffBits; // 位图数据的起始位置,单位是字节
} TBH;

typedef struct tBITMAPINFOHEADER
{
    DWORD biSize; // 信息头大小,一般为 40 字节
    LONG biWidth; // 图像宽度,单位是像素
    LONG biHeight; // 图像高度,单位是像素,如果是正数,表示图像自下向上排列;如果是负数,表示图像自上向下排列
    WORD biPlanes; // 图像数据平面数,一般设置为 1
    WORD biBitCount; // 图像数据位数,一般设置为 24
    DWORD biCompression; // 图像压缩类型,一般设置为 0
    DWORD biSizeImage; // 图像数据大小,单位是字节
    LONG biXPelsPerMeter; // 水平分辨率,单位是像素/米
    LONG biYPelsPerMeter; // 垂直分辨率,单位是像素/米
    DWORD biClrUsed; // 颜色表中实际使用的颜色数,一般为 0
    DWORD biClrImportant; // 颜色表中重要的颜色数,一般为 0
} TBI;


2、BMP 文件中的像素数据的存储方式有点复杂。一般而言,像素数据按照行顺序存储,每一行的像素从左到右依次存储,每个像素按照 BGR 顺序依次存储。每一行的像素数应该满足 4 字节对齐(也就是说,每行像素的字节数应该是 4 的倍数),如果不足 4 字节,则需要用 0 填充。在读取像素数据时,需要先计算每行像素占用的字节数(即 biWidth 像素实际像素占用的字节数,如果不足 4 的倍数则需要补齐),然后按照行顺序读取每个像素的 BGR 值。在写入像素数据时,需要按照行顺序写入每个像素的 BGR 值,注意写入时的字节顺序应该是 BGR。

3、在计算像素灰度直方图时,需要根据像素的 BGR 值进行统计,然后计算各个灰度级别的像素占比。您在代码中使用了浮点数数组来记录灰度级别占比,但是没有将像素灰度值进行归一化,导致计算结果不正确。具体而言,您需要先计算像素总数,然后统计各个灰度级别像素的数量,最后将像素数量除以像素总数即可得到对应的像素占比。

4、最后一个问题是,您的程序没有对 BMP 文件的读取位置进行正确的设置。在读取 BMP 文件时,需要先读取文件头信息块和图像描述信息块,然后才能读取像素数据。在写入 BMP 文件时,需要先将文件头信息块和图像描述信息块写入文件,然后才能写入像素数据。因此,在读取和写入文件时需要使用 fseek 函数来设置读取/写入位置。

修改后的代码

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef unsigned char BYTE;  // 定义BYTE为一个字节的类型
typedef unsigned short WORD; // 定义WORD为两个字节的类型
typedef unsigned int DWORD;  // 定义DWORD为四个字节的类型

#pragma pack(push, 1) // 取消对齐

//14 Bytes 文件头信息块
typedef struct tBITMAPFILEHEADER
{
    WORD bfType;        // 文件类型,应该为 0x4d42
    DWORD bfSize;       // BMP 文件大小,单位是字节
    WORD bfReserved1;   // 保留,必须设置为 0
    WORD bfReserved2;   // 保留,必须设置为 0
    DWORD bfOffBits;    // 位图数据的起始位置,单位是字节
} TBH;

//40 Bytes 图像描述信息块
typedef struct tBITMAPINFOHEADER
{
    DWORD biSize;            // 信息头大小,一般为 40 字节
    int32_t biWidth;         // 图像宽度,单位是像素
    int32_t biHeight;        // 图像高度,单位是像素,如果是正数,表示图像自下向上排列;如果是负数,表示图像自上向下排列
    WORD biPlanes;           // 图像数据平面数,一般设置为 1
    WORD biBitCount;         // 图像数据位数,一般设置为 24
    DWORD biCompression;     // 图像压缩类型,一般设置为 0
    DWORD biSizeImage;       // 图像数据大小,单位是字节
    int32_t biXPelsPerMeter; // 水平分辨率,单位是像素/米
    int32_t biYPelsPerMeter; // 垂直分辨率,单位是像素/米
    DWORD biClrUsed;         // 颜色表中实际使用的颜色数,一般为 0
    DWORD biClrImportant;    // 颜色表中重要的颜色数,一般为 0
} TBI;

typedef struct PerPixelgbr
{
    BYTE b;
    BYTE g;
    BYTE r;
} BGR;

int main()
{
    FILE* in;
    FILE* out;
    TBH BITMAPFILEHEADER;
    TBI BITMAPINFOHEADER;
    BGR* pixels;

    fopen_s(&in, "input.bmp", "rb");
    if (!in) {
        printf("Error opening input file!\n");
        return 1;
    }

    // 读取文件头信息块
    fread(&BITMAPFILEHEADER, sizeof(TBH), 1, in);
    if (BITMAPFILEHEADER.bfType != 0x4d42) {
        printf("Error: Not a BMP file!\n");
        fclose(in);
        return 1;
    }

    // 读取图像描述信息块
    fread(&BITMAPINFOHEADER, sizeof(TBI), 1, in);

    // 计算像素数据大小
    uint32_t imageSize = BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight * 3;
    // 计算每行字节对齐所需的填充字节数
uint32_t paddingSize = (4 - (imageSize % 4)) % 4;

// 分配像素数据缓存区
pixels = (BGR*)malloc(imageSize);

// 读取像素数据
fread(pixels, 1, imageSize, in);

fclose(in);

// 对每个通道进行直方图均衡化
uint32_t B_ZFT[256] = { 0 }, G_ZFT[256] = { 0 }, R_ZFT[256] = { 0 };
double B_GL[256], G_GL[256], R_GL[256];

for (int i = 0; i < BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight; i++)
{
    B_ZFT[pixels[i].b]++;
    G_ZFT[pixels[i].g]++;
    R_ZFT[pixels[i].r]++;
}

double gailvhe = 0;
for (int i = 0; i < 256; i++)
{
    gailvhe += (double)B_ZFT[i] / ((double)BITMAPINFOHEADER.biWidth * (double)BITMAPINFOHEADER.biHeight);
    B_GL[i] = gailvhe;
}
gailvhe = 0;
for (int i = 0; i < 256; i++)
{
    gailvhe += (double)G_ZFT[i] / ((double)BITMAPINFOHEADER.biWidth * (double)BITMAPINFOHEADER.biHeight);
    G_GL[i] = gailvhe;
}
gailvhe = 0;
for (int i = 0; i < 256; i++)
{
    gailvhe += (double)R_ZFT[i] / ((double)BITMAPINFOHEADER.biWidth * (double)BITMAPINFOHEADER.biHeight);
    R_GL[i] = gailvhe;
}

uint32_t B_CS[256], G_CS[256], R_CS[256];
for (int i = 0; i < 256; i++)
{
    B_CS[i] = (uint32_t)(B_GL[i] * 255);
    G_CS[i] = (uint32_t)(G_GL[i] * 255);
    R_CS[i] = (uint32_t)(R_GL[i] * 255);
}

// 创建输出文件
fopen_s(&out, "output.bmp", "wb");
if (!out) {
    printf("Error creating output file!\n");
    free(pixels);
    return 1;
}

// 写入文件头信息块
fwrite(&BITMAPFILEHEADER, sizeof(TBH), 1, out);

// 写入图像描述信息块
fwrite(&BITMAPINFOHEADER, sizeof(TBI), 1, out);

// 写入像素数据
for (int i = 0; i < BITMAPINFOHEADER.biWidth * BITMAPINFOHEADER.biHeight; i++)
{
    pixels[i].b = B_CS[pixels[i].b];
    pixels[i].g = G_CS[pixels[i].g];
    pixels[i].r = R_CS[pixels[i].r];
}

fwrite(pixels, 1, imageSize, out);

// 写入填充字节
BYTE padding[3] = { 0 };
for (int i = 0; i < paddingSize; i++) {
    fwrite(padding, 1, 1, out);
}

fclose(out);
free(pixels);

printf("Done!\n");
return 0;
}


参考GPT和自己的思路,从您提供的代码中,我们发现了一些可能导致无法显示图像的问题:

1.程序没有为读取的图像像素数据开辟内存空间,所以没有存储像素数据。在实现直方图均衡化之前,需要读取图像像素数据并将其存储在内存中。

2.程序中没有处理图像像素数据。直方图均衡化算法需要对读取的图像像素数据进行处理,从而获得均衡化后的像素数据。

3.程序中没有将处理后的像素数据写回到输出文件中。在处理完像素数据后,需要将像素数据写回到输出文件中,才能显示处理后的图像。

下面是对您提供的代码的修改建议:

在读取图像像素数据前,需要先为像素数据开辟内存空间,可以使用如下代码:

int imageSize = BITMAPINFOHEADER->biSizeImage;
BYTE* imageData = (BYTE*)malloc(imageSize);
fread(imageData, sizeof(BYTE), imageSize, in);


对于读取的图像像素数据,需要将其转化为 BGR 格式的像素数据,可以使用如下代码:

BGR* pixels = (BGR*)malloc(sizeof(BGR) * W * H);
for (int i = 0; i < H; i++) {
    for (int j = 0; j < W; j++) {
        int index = i * W + j;
        int offset = (H - i - 1) * W * 3 + j * 3;
        pixels[index].b = imageData[offset];
        pixels[index].g = imageData[offset + 1];
        pixels[index].r = imageData[offset + 2];
    }
}


在对像素数据进行处理后,需要将处理后的像素数据写回到输出文件中,可以使用如下代码:

fwrite(imageData, sizeof(BYTE), imageSize, out);
不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^