0

这个问题对于使用纯 C 进行图像处理有更多的理解是具有挑战性的。我已经做了一个简单的程序,使用用 GCC 编译的 C 来读取非二进制 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]); 
  }
}

我已经阅读了一些与我的问题相关的页面 [如何阅读 .PGM 格式文件?] ,但我找不到任何明确而简单的答案。对于我的代码中的任何错误,我深表歉意。任何关于我的代码的建议和批评都将不胜感激。

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

编辑我

编辑二

4

3 回答 3

1

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

一些评论:

  • 不要硬编码宽度和高度,您应该使用标题中的信息
  • 检查幻数,如果不是 P5 则抛出/返回错误(或正确处理)
  • 该文件可能有行注释,请考虑
于 2011-09-17T14:24:55.440 回答
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 上(我假设您正在运行 Windows,因为您使用的是 Irfan View),您需要明确声明您需要二进制 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 字节的值。
于 2011-09-17T15:53:04.243 回答
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);
}
于 2011-09-17T15:59:31.823 回答