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

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

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

我目前的代码是:

#include  #include  #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]); } } 

我已经阅读了一些与我的问题相关的页面[ 如何阅读.PGM格式文件? ],但我找不到任何清晰简单的答案。 我为我的代码中的任何错误道歉。 任何有关我的代码的建议和批评者将不胜感激。

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

编辑我

  • 添加PGM图像。 PGM图像样本
  • 添加function以创建图像直方图。

编辑二

  • 再添加一个fgets ,请参阅示例图像 。

我上面的脚本无法显示正确的颜色直方图,因为如果你理性思考,你可能会得到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字节值。

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

一些评论:

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

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

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

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

 #include  #include  #include  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); }