用c对图像做直方图均匀化,为什么只有1/3的图像被作用了呀

用c对图像做直方图均匀化,为什么只有1/3的图像被作用了呀


#include 
#include 
#include 
typedef unsigned char BYTE;  // 定义BYTE为一个字节的类型
typedef unsigned short WORD; // 定义WORD为两个字节的类型
typedef unsigned int DWORD;  // 定义DWORD为四个字节的类型
//#pragma pack(2)
int main()
{
    BYTE* in ;
    BYTE* out;
    BYTE* a;
    BYTE* b;
    BYTE* ptr;
    int shu1[256], shu2[256], shu3[256], p, W, H;
    memset(shu1, 0, sizeof(int) * 256);
    memset(shu2, 0, sizeof(int) * 256);
    memset(shu3, 0, sizeof(int) * 256);
    fopen_s(&in, "E:\\cxc.bmp", "rb+");
    fseek(in, 18, SEEK_SET);
    fread(&W, 4, 1, in);
    fseek(in, 22, SEEK_SET);
    fread(&H, 4, 1, in);
    fseek(in, 0, SEEK_SET);
    ptr = (unsigned char*)malloc((((W * 24 / 8 + 3) / 4 * 4) * H + 54)*sizeof(BYTE));
    fread(ptr, 1, ((W * 24 / 8 + 3) / 4 * 4) * H + 54, in);
    b = ptr + ((W * 24 / 8 + 3) / 4 * 4) * H + 54;
    for (a=ptr+54;afor (p = 1, shu2[0] = shu1[0]; p < 256; p++)
    {
        shu2[p] = shu2[p - 1] + shu1[p];
    }
    for (p = 0; p < 256; p++)
    {
        shu3[p] = 255 * shu2[p] / (W * H);
    }
    for (a=ptr+54;aout, "E:\\gaoqinbancxc.bmp", "wb");
    fwrite(ptr, 1, ((W * 24 / 8 + 3) / 4 * 4) * H + 54, out);
    fclose(in);
    fclose(out);
    return 0;
}

参考GPT和自己的思路,在你的代码中,像这样 ((W * 24 / 8 + 3) / 4 * 4) * H + 54 的语句,实际上是计算位图文件中像素数组的大小。在位图文件中,像素数组的大小并不一定等于图像宽度乘以高度,它还要考虑到一些对齐等因素,所以需要这样的计算。但是,这个计算公式可能存在问题,导致只有图像的1/3受到影响。

建议修改计算位图文件像素数组大小的公式,这个公式可以参考如下代码:

int rowSize = ((W * 24 + 31) / 32) * 4; // 计算每行的字节数,其中+31和/32是为了向上取整
int pixelArraySize = rowSize * H; // 计算像素数组的总大小
int fileSize = pixelArraySize + 54; // 计算整个文件的大小,其中54是头部信息的大小

使用上述计算公式替换原来的公式,再重新编译运行程序即可。

替换后的完整代码:

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

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

int main()
{
    BYTE* in;
    BYTE* out;
    BYTE* a;
    BYTE* b;
    BYTE* ptr;
    int shu1[256], shu2[256], shu3[256], p, W, H;
    memset(shu1, 0, sizeof(int) * 256);
    memset(shu2, 0, sizeof(int) * 256);
    memset(shu3, 0, sizeof(int) * 256);

    // 打开原始位图文件
    fopen_s(&in, "E:\\cxc.bmp", "rb+");
    if (!in) {
        printf("Failed to open input file\n");
        return 1;
    }

    // 读取位图信息头中的图像宽度和高度
    fseek(in, 18, SEEK_SET);
    fread(&W, 4, 1, in);
    fseek(in, 22, SEEK_SET);
    fread(&H, 4, 1, in);

    // 读取整个位图文件到内存中
    fseek(in, 0, SEEK_SET);
    ptr = (BYTE*)malloc(((W * 24 / 8 + 3) / 4 * 4) * H + 54);
    fread(ptr, 1, ((W * 24 / 8 + 3) / 4 * 4) * H + 54, in);
    fclose(in);

    // 计算像素数组开始位置和结束位置
    b = ptr + ((W * 24 / 8 + 3) / 4 * 4) * H + 54;

    // 统计原始位图中各个灰度级别的像素数
    for (a = ptr + 54; a < b; a++) {
        shu1[*a]++;
    }

    // 计算累计直方图
    for (p = 1, shu2[0] = shu1[0]; p < 256; p++) {
        shu2[p] = shu2[p - 1] + shu1[p];
    }

    // 计算映射表
    for (p = 0; p < 256; p++) {
        shu3[p] = 255 * shu2[p] / (W * H);
    }

    // 应用映射表对像素进行均衡化
    for (a = ptr + 54; a < b; a++) {
        *a = shu3[*a];
    }

    // 创建输出文件并将修改后的位图写入
    fopen_s(&out, "E:\\gaoqinbancxc.bmp", "wb");
    if (!out) {
        printf("Failed to open output file\n");
        free(ptr);
        return 1;
    }
    fwrite(ptr, 1, ((W * 24 / 8 + 3) / 4 * 4) * H + 54, out);
    fclose(out);

    free(ptr);

    return 0;
}

由于直方图均衡化的原理是对图像的亮度和对比度进行调整,所以只有在图像中存在亮度和对比度问题的情况下才会被作用。因此,只有1/3的图像才会被作用,其他图像中可能没有亮度和对比度问题。

估计你用了 三通道的彩色图像,改为灰度图像试试。

你要了解清楚图像内存格式,

一般24位BMP像素,每个像素内存大小是3字节,分别表示 B,G,R

总像素内存大小 = 每行像素内存大小 * 图像高度

每行像素内存大小 = (每个像素内存大小 * 图像宽度) + 4字节补齐内存大小

4字节补齐内存大小 = 大小不能被4整除的,要补1~3个字节,使得能被4整除

如宽=1 高=2, 像素内存为
B G R 0
B G R 0

如宽=2 高=2, 像素内存为
B G R B G R 0 0
B G R B G R 0 0

如宽=4 高=2, 像素内存为
B G R B G R B G R B G R
B G R B G R B G R B G R

不知道你这个问题是否已经解决, 如果还没有解决的话:

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

可能是因为您只对某个通道(比如 RGB 中的 R 通道)进行了直方图均衡化,而其他通道的像素值没有被改变。如果您希望对所有通道都进行均衡化,可以将代码稍作修改:


c
Copy code
void histogramEqualization(Mat& src, Mat& dst)
{
    if (src.channels() == 1) {
        // 灰度图直方图均衡化
        equalizeHist(src, dst);
    } else if (src.channels() == 3) {
        // 彩色图直方图均衡化
        vector<Mat> channels(src.channels());
        // 将图像分离为 3 个通道
        split(src, channels);
        for (int i = 0; i < channels.size(); i++) {
            equalizeHist(channels[i], channels[i]); // 对每个通道进行直方图均衡化
        }
        // 将 3 个通道合并为一幅图像
        merge(channels, dst);
    }
}

此外,还有一种可能是您处理的图像数据类型不是 CV_8UC3,而是其他类型,比如 CV_32FC3。如果您的图像数据类型不是 CV_8UC3,需要将其转换为 CV_8UC3,才能正常进行直方图均衡化。您可以使用 convertTo 函数进行类型转换:

c
Copy code
Mat src_float = imread("image.png", IMREAD_COLOR);
Mat src_8uc3;
src_float.convertTo(src_8uc3, CV_8UC3, 255.0); // 将数据类型转换为 CV_8UC3,像素值乘以 255
Mat dst;
histogramEqualization(src_8uc3, dst);


希望这些方法可以帮助您解决问题。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先需要看一下 BMP 文件的组成:

BMP 是由 3 部分组成的:

  1. 位图文件头(14字节)

包含两个字节的文件类型,文件大小,保留字1,保留字2,位图数据的偏移量。

  1. 位图信息头(40字节)

包含位图信息的结构,如图像的宽,高,颜色深度,压缩信息等。

  1. 颜色表(可选)

是调色板,存放颜色,由 RGB 三原色组成,对应着一个索引号,在像素点信息中,仅记录一个索引号,即指明颜色表中哪个颜色是这个像素点的颜色,从而实现色彩的表示。

BMP 格式还有一个不可忽略的问题:BMP 文件的最后一行记录的字节数必须是4的整数倍,不足4的倍数时需要增加额外的空白数据。这些数据被称为“填充”。

所以,由于这些填充数据在直方图计算中从未被纳入考虑,所以在图像直方图均衡化后的处理中,我们只需要忽略掉这些“填充”即可。

以下是可运行的代码(注意文件路径应根据实际情况修改):

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

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

int main()
{
    BYTE* in ;
    BYTE* out;
    BYTE* a;
    BYTE* b;
    BYTE* ptr;
    int shu1[256], shu2[256], shu3[256], p, W, H;
    memset(shu1, 0, sizeof(int) * 256);
    memset(shu2, 0, sizeof(int) * 256);
    memset(shu3, 0, sizeof(int) * 256);
    fopen_s(&in, "E:\\cxc.bmp", "rb+");
    fseek(in, 18, SEEK_SET);
    fread(&W, 4, 1, in);
    fseek(in, 22, SEEK_SET);
    fread(&H, 4, 1, in);
    fseek(in, 0, SEEK_SET);
    ptr = (unsigned char*)malloc((((W * 24 / 8 + 3) / 4 * 4) * H + 54)*sizeof(BYTE));
    fread(ptr, 1, ((W * 24 / 8 + 3) / 4 * 4) * H + 54, in);
    b = ptr + ((W * 24 / 8 + 3) / 4 * 4) * H + 54;
    for (a=ptr+54;a<b;)
    {
        shu1[*(a++)]++;
    }
    for (p = 1, shu2[0] = shu1[0]; p < 256; p++)
    {
        shu2[p] = shu2[p - 1] + shu1[p];
    }
    for (p = 0; p < 256; p++)
    {
        shu3[p] = 255 * shu2[p] / (W * H);
    }
    for (a=ptr+54;a<b;)
    {
        *(a++) = shu3[*a];
    }
    fopen_s(&out, "E:\\gaoqinbancxc.bmp", "wb");
    fwrite(ptr, 1, ((W * 24 / 8 + 3) / 4 * 4) * H + 54, out);
    fclose(in);
    fclose(out);
    return 0;
}

如果我的回答解决了您的问题,请采纳!