使用Pure C从PGM图像(p5)显示直方图像素值,不带任何图像处理库

时间:2011-09-17 14:07:07

标签: c image binary pgm

这个问题对于使用纯C更好地理解图像处理是一项挑战。我已经完成了一个简单的程序,使用C编译的GCC读取非二进制PGM文件。现在,当我尝试读取二进制PGM文件时,它正成为一个问题。可以使用IrvanView JPG 转换为 PGM 来获取此二进制PGM文件。

注意:请不要回答任何图像处理库(例如:OpenCV)。

我目前的代码是:

#include    <stdio.h> 
#include    <stdlib.h>
#define WIDTH 1024  
#define HEIGHT 768
#define READ_IMAGE_NAME "MY_PGM_FILE_NAME.pgm"

void print_histogram_table(int *histog);

main() {
  FILE *fp;

  int i,j, height= HEIGHT, width=WIDTH;
  char line[100];

  // Color depth is 255. 
  unsigned char pixel_value;

  fp = fopen(READ_IMAGE_NAME,"r");

  // get the first four lines.
  fgets (line,100,fp); 
  fgets (line,100,fp);
  fgets (line,100,fp);
  fgets (line,100,fp);

  // Histogram helper
  int histo[65536];
  int x;
  for ( x =0; x < 65536; x++) {    
        histo[x] = 0;
  }

  for(j=0;j<height;j++) {
     for(i=0;i<width;i++) {
         fread(&pixel_value, sizeof(unsigned char), 1, fp);
         // Turn on the code below, if you want to check color on specific row and column.
//             printf("row num. %d column num. %d    pixel value=%d\n",j,i,pixel_value);  

       histo[pixel_value]++;
     }
  }

  // Make histogram
  print_histogram_table(histo);

  fclose(fp);       
  getch();
}

void print_histogram_table(int *histog)
{
  int x; 
  for (x= 0; x < 255; x++) {
     if ( histog[x] != 0)
        printf("Color number %d count %d\n", x, histog[x]); 
  }
}

我已经阅读了一些与我的问题相关的页面[How to read .PGM format file?],但我找不到任何明确而简单的答案。我为我的代码中的任何错误道歉。任何有关我的代码的建议和批评者将不胜感激。

我上面的脚本无法显示正确的颜色直方图,因为如果你理性思考,你可能会得到100以上的像素颜色(不仅低于100)。那么,主要问题是如何解决这个问题?

编辑我

编辑II

3 个答案:

答案 0 :(得分:1)

而且......问题是什么?您的代码看起来几乎可以读取二进制PGM(P5?),它只是缺少对每个像素数据行的换行检查(根据维基百科文章,ASCII和二进制格式在每个像素的末尾都有换行符数据行)。

一些意见:

  • 不要硬编码宽度和高度,您应该使用标题中的信息
  • 检查幻数,抛出/返回错误,如果它不是P5(或正确处理)
  • 该文件可能有行注释,请考虑

答案 1 :(得分:1)

  

我上面的脚本无法显示正确的颜色直方图,因为如果你理性思考,你可能会得到100以上的像素颜色(不仅低于100)。所以,主要的问题是如何解决这个问题?

我假设你的意思是:

  

现在,在链接图像上运行此程序仅显示100以下像素值的非零直方图条目。我知道图像中至少有一个像素值超过100,所以我的代码中有一个错误。任何人都可以帮我弄清楚为什么会这样吗?

我建议你重新说出你的问题,以明确这一点。

代码表面看起来还不错,假设您只希望它能够处理您链接到的图像。但是,有很多小错误可能会加起来。以下是一些建议:

直方图大小

首先,您无法打印整个直方图。考虑将直方图大小传递给打印直方图的函数。此外,即使的大小匹配(均为256),您仍然会有错误。你永远不会打印第256个值。

int histo[65536];
// ...
print_histogram_table(histo);
// ...
void print_histogram_table(int *histog)
{
    int x; 
    for (x= 0; x < 255; x++) {
       if ( histog[x] != 0)
          printf("Color number %d count %d\n", x, histog[x]); 
}

二进制I / O

打开二进制文件时需要指定“二进制”I / O.在UNIX上,这通常没有区别,因为它是默认模式。但是,在Windows上(我假设您正在使用Irfan View时运行Windows),您需要明确声明您需要二进制I / O.

这是第一次处理二进制文件时常见的错误。基本上,一旦fread()调用捕获EOF字节,它将停止读取文件,您将获得所有后续读取的垃圾值(可能是最后一次读取的副本)真正的字节),这意味着你实际上并没有阅读整个图像。

fp = fopen(READ_IMAGE_NAME,"r");
// change to:
fp = fopen(READ_IMAGE_NAME,"rb");

其他小问题

您的代码无法处理大量内容:

  1. PNM文件可能在文件开头有评论
  2. 行数可能超过100个字符。
  3. 图片尺寸不强制为1024x768
  4. 在程序中对文件名进行硬编码并不是很有用,即使只是测试代码也是如此。
  5. 如果你真的得到一个每像素16位的灰度图像,你的直方图足够大,但你应该读取2字节值。

答案 2 :(得分:0)

关于您的计划的一些注意事项:

  • 您应该阅读标题,而不是硬编码宽度,高度和最大像素值。
  • 将整个图像加载到内存并从内存进行处理
  • 的方式更有效
  • PGM文件是二进制文件,你应该在fopen中使用“rb”(以防你在Windows上运行它,其中二进制文本与文本不同)
  • 您的直方图打印功能缺少最后一个像素(255是有效像素值)
  • 不确定你的直方图数组为何达到64K,你是否计划支持16位PGM?
  • 您应该进行错误处理以避免崩溃或其他意外行为

以下是您的计划的改进版本,上述要点已得到解决。我希望它有所帮助。

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

int main(int argc, char* argv[])
{
    char line[100];
    int width, height, maxval;
    unsigned char* image;
    unsigned int* histo;
    int i;
    FILE *fd;

    fd = fopen(argv[1], "rb");
    if (fd == NULL) {
        printf("could not open image file\n");
        exit(1);
    }

    // magic number
    fgets(line, sizeof(line), fd);
    if (strcmp(line, "P5\n") != 0) {
        printf("image is not in PGM format!\n");
        fclose(fd);
        exit(1);
    }

    // skip comment
    fgets(line, sizeof(line), fd);

    // read header
    if (fscanf(fd, "%d %d %d\n", &width, &height, &maxval) != 3) {
        printf("invalid header\n");
        fclose(fd);
        exit(1);
    }
    if (maxval > 255) {
        printf("sorry, only 8-bit PGMs are supported at this time!\n");
        fclose(fd);
        exit(1);
    }

    printf("width: %d\nheight: %d\nmaxval: %d\n", width, height, maxval);

    // read image data
    image = malloc(width * height);
    if (fread(image, sizeof(unsigned char), width * height, fd) != width * height) {
        printf("could not read image\n");
        fclose(fd);
        exit(1);
    }
    fclose(fd);

    // compute histogram
    histo = (int*)calloc(maxval+1, sizeof(int));
    for (i = 0; i < width * height; i++)
        histo[image[i]]++;

    // print histogram
    for (i = 0; i <= maxval; i++)
        printf("Color number %d count %d\n", i, histo[i]);

    // release memory
    free(image);
    free(histo);
}