scratch lenet(2): C语言实现图像直方图的计算

scratch lenet(2): C语言实现图像直方图的计算

1. 目的

用 C 语言实现 uint8 类型图像(单通道)的直方图计算。不涉及直方图均衡化。

2. 什么是图像直方图

2.1 统计得到图像直方图

通常是对于单通道的灰度图而言的。像素范围是 [0, 255], 统计每个像素出现的次数, 存放到一个 256 元素的一维数组 hist 中。换言之, hist 是一个统计结果。

void calculate_histogram(uchar* image, int width, int height, int hist[256])
{
    memset(hist, 0, 256 * sizeof(int));
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int idx = i * width + j;
            uchar v = image[idx];
            hist[v]++;
        }
    }

    printf("[DEBUG] print histogram:\n");
    for (int i = 0; i < 256; i++)
    {
        if (hist[i] > 0)
        {
            printf("gray value=%d, occurance cnt=%d\n", i, hist[i]);
        }
    }
}

2.2 可视化:直方图转为图像表示

所谓可视化直方图, 是说把原本是1维的、256 元素的数组 hist[256], 转为2维图像。 图像宽度是256, 高度是 max(hist[i]).

在计算机视觉中, 白色对应到255,黑色对应到0.

在计算机视觉的C/C++实现中,图像坐标原点是左上角, 而数学中的绘制图像曲线,左下角才是原点。

于是, 需要在获取图像坐标点 P(i, j) 时, 执行坐标变换,得到以左下角为原点时的坐标 P'(inv_i, j)

P' 的取值为 0 或 255: 如果比 hist[j] 要大(坐标点更高),则为255; 否则为0.

typedef struct GrayImage
{
    int width;
    int height;
    uchar* data;
} GrayImage;

GrayImage create_gray_image_from_histogram(int hist[256])
{
    // draw histogram as image
    int max_hist = 0;
    for (int i = 0; i < 256; i++)
    {
        if (hist[i] > max_hist)
        {
            max_hist = hist[i];
        }
    }
    int hist_height = max_hist;
    int hist_width = 256;
    uchar* image = (uchar*)malloc(hist_height * hist_width);
    for (int i = 0; i < hist_height; i++)
    {
        int inv_i = hist_height - 1 - i;
        for (int j = 0; j < hist_width; j++)
        {
            int idx = i * hist_width + j;
            if (inv_i > hist[j])
            {
                image[idx] = 255;
            }
            else
            {
                image[idx] = 0;
            }
        }
    }

    GrayImage hist_image;
    hist_image.width = hist_width;
    hist_image.height = hist_height;
    hist_image.data = image;
    return hist_image;
}

2.3 可视化:保存结果图

使用 .pgm 格式。使用 scratch lenet(1): 读写 pgm 图像文件 中实现的 .pgm 图像读写函数。

void write_pgm_image(uchar* image, int width, int height, const char* filename)
{
    FILE* fout = fopen(filename, "wb");
    fprintf(fout, "P5\n%d %d\n255\n", width, height);
    fwrite(image, width * height, 1, fout);
    fclose(fout);
}

3. 完整代码和结果

#include 
#include 

typedef unsigned char uchar;

void write_pgm_image(uchar* image, int width, int height, const char* filename)
{
    FILE* fout = fopen(filename, "wb");
    fprintf(fout, "P5\n%d %d\n255\n", width, height);
    fwrite(image, width * height, 1, fout);
    fclose(fout);
}

void* memset(void* s, int c, size_t n)
{
    char x = c & 0xff;
    char* p = (char*)s;
    for (int i = 0; i < n; i++)
    {
        p[i] = n;
    }
    return s;
}

void calculate_histogram(uchar* image, int width, int height, int hist[256])
{
    memset(hist, 0, 256 * sizeof(int));
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            int idx = i * width + j;
            uchar v = image[idx];
            hist[v]++;
        }
    }

    printf("[DEBUG] print histogram:\n");
    for (int i = 0; i < 256; i++)
    {
        if (hist[i] > 0)
        {
            printf("gray value=%d, occurance cnt=%d\n", i, hist[i]);
        }
    }
}

typedef struct GrayImage
{
    int width;
    int height;
    uchar* data;
} GrayImage;

GrayImage create_gray_image_from_histogram(int hist[256])
{
    // draw histogram as image
    int max_hist = 0;
    for (int i = 0; i < 256; i++)
    {
        if (hist[i] > max_hist)
        {
            max_hist = hist[i];
        }
    }
    int hist_height = max_hist;
    int hist_width = 256;
    uchar* image = (uchar*)malloc(hist_height * hist_width);
    for (int i = 0; i < hist_height; i++)
    {
        int inv_i = hist_height - 1 - i;
        for (int j = 0; j < hist_width; j++)
        {
            int idx = i * hist_width + j;
            if (inv_i > hist[j])
            {
                image[idx] = 255;
            }
            else
            {
                image[idx] = 0;
            }
        }
    }

    GrayImage hist_image;
    hist_image.width = hist_width;
    hist_image.height = hist_height;
    hist_image.data = image;
    return hist_image;
}

int main()
{
    uchar image[8 * 8] = {
        52, 55, 61, 66, 70, 61, 64, 73,
        63, 59, 55, 90, 109, 85, 69, 72,
        62, 59, 68, 113, 144, 104, 66, 73,
        63, 58, 71, 122, 154, 106, 70, 69,
        67, 61, 68, 104, 126, 88, 68, 70,
        79, 65, 60, 70, 77, 68, 58, 75,
        85, 71, 64, 59, 55, 61, 65, 83,
        87, 79, 69, 68, 65, 76, 78, 94
    };
    int width = 8;
    int height = 8;
    int hist[256] = { 0 };

    calculate_histogram(image, width, height, hist);
    GrayImage hist_image = create_gray_image_from_histogram(hist);
    write_pgm_image(hist_image.data, hist_image.width, hist_image.height, "histogram.pgm");
    free(hist_image.data);
}

运行结果

在这里插入图片描述

4. References

  1. 直方图均衡化(HE)

你可能感兴趣的:(C/C++,c语言,开发语言,图像处理,深度学习)