0

この質問は、純粋な C を使用した画像処理について理解を深める上で難しい問題です。GCC でコンパイルされた C を使用して、非バイナリ PGM ファイルを読み取る簡単なプログラムを作成しました。現在、バイナリ PGM ファイルを読み込もうとすると問題が発生しています。このバイナリ PGM ファイルは、 IrvanViewを使用してJPGPGMに変換することで取得できます。

注:画像処理ライブラリ (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 未満だけではない) 可能性があるからです。したがって、主な質問は、この問題を解決するにはどうすればよいですか?

編集私

  • PGM イメージを追加します。PGM 画像サンプル
  • 画像ヒストグラムを作成する機能を追加。

編集Ⅱ

4

3 に答える 3

1

上記のスクリプトでは、正しい色のヒストグラムを表示できませんでした。合理的に考えれば、ピクセルの色が 100 を超える (100 未満だけではない) 可能性があるためです。したがって、主な質問は、この問題を解決するにはどうすればよいですか?

私はあなたが意味すると仮定しています:

現在、リンクされた画像でこのプログラムを実行すると、100 未満のピクセル値の非ゼロ ヒストグラム エントリのみが表示されます。100 を超える画像のピクセル値が少なくとも 1 つあることがわかっているため、コードにバグがあります。なぜこれが起こるのかを理解するのを手伝ってくれる人はいますか?

これを明確にするために、質問を言い換えることをお勧めします。

リンク先の画像でのみ動作させたいと仮定すると、コードは表面的には問題ないように見えます。ただし、追加される可能性のある小さな間違いがたくさんあります。以下にいくつかの提案を示します。

ヒストグラムのサイズ

まず、ヒストグラム全体を印刷することはできません。ヒストグラムを出力する関数にヒストグラム サイズを渡すことを検討してください。さらに、サイズ一致した場合でも (両方とも 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 バイトの値を読み取る必要があります。
于 2011-09-17T15:53:04.243 に答える
1

そして...質問は何ですか?あなたのコードは、バイナリ PGM (P5?) を読み取るのにほとんど問題ないように見えますが、各ピクセル データ行の改行チェックが欠落しているだけです (ウィキペディアの記事によると、ASCII 形式とバイナリ形式の両方に、各ピクセル データ行の末尾に改行があります)。

いくつかのコメント:

  • 幅と高さをハードコーディングしないでください。代わりにヘッダーからの情報を使用する必要があります
  • マジックナンバーを確認し、P5でない場合はエラーを投げる(または正しく処理する)
  • ファイルには行コメントが含まれている可能性があります。
于 2011-09-17T14:24:55.440 に答える
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 に答える