2017-01-02 5 views
1

C言語のlibpngとPythonのNumPyでRGB三刺激値空間の平均ベクトルを計算しようとしていますが、それぞれ異なる結果が得られます。私はPythonがthis imageの正しい結果を[ 127.5 127.5 0. ]で与えていると確信しています。しかし、Cの次のブロックでは、私は[ 38.406494 38.433670 38.459641 ]の奇妙な結果を得ます。私は何も与えずに何週間も自分のコードを見つめていたので、他の人がアイデアを持っているかどうかは分かりました。平均ベクトル計算のlibpngとnumpyの結果が異なります

また、私はこのコードを他の画像でテストしましたが、同様の不名誉な結果が得られます。 3つの数字はすべて通常最初の4桁の数字と一致するので、それは非常に興味があります。私はこれを引き起こしているのかどうかはわかりません。

/* See if our average vector matches that of Python's */ 

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <png.h> 

// For getting the PNG data and header/information back 
typedef struct 
{ 
    uint32_t width;   // width of image 
    uint32_t height;  // height of image 
    int bit_depth;   // bits/pixel component (should be 8 in RGB) 
    png_bytep datap;  // data 
} rTuple; 

#define PNG_BYTES_TO_CHECK 8 
#define CHANNELS 3 

int 
check_PNG_signature(unsigned char *buffer) 
{ 
    unsigned i; 
    const unsigned char signature[8] = { 0x89, 0x50, 0x4e, 0x47, 
             0x0d, 0x0a, 0x1a, 0x0a }; 
    for (i = 0; i < PNG_BYTES_TO_CHECK; ++i) 
    { 
     if (buffer[i] != signature[i]) 
     { 
      fprintf(stderr, "** File sig does not match PNG, received "); 
      for (i = 0; i < PNG_BYTES_TO_CHECK; ++i) 
       fprintf(stderr, "%.2X ", buffer[i]); 
      fprintf(stderr, "\n"); 
      abort(); 
     } 
    } 
    return 1; 
} 

rTuple 
read_png_file(char *file_name) 
{ 
    /* Get PNG data - I've pieced this together by reading `example.c` from 
     beginning to end */ 
    printf("** Reading data from %s\n", file_name); 

    png_uint_32 width, height; // holds width and height of image 

    uint32_t row; // for iteration later 
    int bit_depth, color_type, interlace_type; 

    unsigned char *buff = malloc(PNG_BYTES_TO_CHECK * sizeof(char)); 
    memset(buff, 0, PNG_BYTES_TO_CHECK * sizeof(char)); 

    FILE *fp = fopen(file_name, "rb"); 
    if (fp == NULL) abort(); 

    if (fread(buff, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) { 
     fprintf(stderr, "** Could not read %d bytes\n", PNG_BYTES_TO_CHECK); 
     abort(); 
    } 

    check_PNG_signature(buff); 
    rewind(fp); 

    // create and initialize the png_struct, which will be destroyed later 
    png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING 
     , NULL /* Following 3 mean use stderr & longjump method */ 
     , NULL 
     , NULL 
    ); 
    if (!png_ptr) abort(); 

    png_infop info_ptr = png_create_info_struct(png_ptr); 
    if (!info_ptr) abort(); 

    // following I/O initialization method is required 
    png_init_io(png_ptr, fp); 
    png_set_sig_bytes(png_ptr, 0); // libpng has this built in too 

    // call to png_read_info() gives us all of the information from the 
    // PNG file before the first IDAT (image data chunk) 
    png_read_info(png_ptr, info_ptr); 

    // Get header metadata now 
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 
     &interlace_type, NULL, NULL); 

    // Scale 16-bit images to 8-bits as accurately as possible (shouldn't be an 
    // issue though, since we're working with RGB data) 
#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED 
    png_set_scale_16(png_ptr); 
#else 
    png_set_strip_16(png_ptr); 
#endif 

    png_set_packing(png_ptr); 

    // PNGs we're working with should have a color_type RGB 
    if (color_type == PNG_COLOR_TYPE_PALETTE) 
     png_set_palette_to_rgb(png_ptr); 

    // Required since we selected the RGB palette 
    png_read_update_info(png_ptr, info_ptr); 

    // Allocate memory to _hold_ the image data now (lines 547-) 
    png_bytep row_pointers[height]; 

    for (row = 0; row < height; ++row) 
     row_pointers[row] = NULL; 

    for (row = 0; row < height; ++row) 
     row_pointers[row] = png_malloc(png_ptr,\ 
      png_get_rowbytes(png_ptr, info_ptr) 
     ); 

    png_read_image(png_ptr, row_pointers); 
    png_read_end(png_ptr, info_ptr); 

    // Now clean up - the image data is in memory 
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 
    fclose(fp); 

    rTuple t = { width, height, bit_depth, *row_pointers }; 

    return t; 
} 

int 
main(int argc, char *argv[]) 
{ 
    if (argc != 2) { 
     printf("** Provide filename\n"); 
     abort(); 
    } 

    char *fileName = argv[1]; 

    // get data read 
    rTuple data = read_png_file(fileName); 

    /* let's try computing the absolute average vector */ 
    uint32_t i, j, k; 
    double *avV = malloc(CHANNELS * sizeof(double)); 
    memset(avV, 0, sizeof(double) * CHANNELS); 

    double new_px[CHANNELS]; 
    png_bytep row, px; 
    for (i = 0; i < data.height; ++i) 
    { 
     row = &data.datap[i]; 
     for (j = 0; j < data.width; ++j) 
     { 
      px = &(row[j * sizeof(int)]); 

      for (k = 0; k < CHANNELS; ++k) { 
       new_px[k] = (double)px[k]; 
       avV[k] += new_px[k]; 
      } 
     } 
    } 

    double size = (double)data.width * (double)data.height; 

    for (k = 0; k < CHANNELS; ++k) { 
     avV[k] /= size; 
     printf("channel %d: %lf\n", k + 1, avV[k]); 
    } 

    printf("\n"); 

    return 0; 
} 

は今はPythonで私はちょうど上記の私の結果が得られ、単純なコンテキストマネージャとコンピューティング np.mean(image_data, axis=(0, 1))、で画像を開いています。

答えて

1

基本的には、あなたのコードをGithub gistと比較して見つけようとする2つのバグ(libpng側とポインタ算術)がありました。以下のリストは、同じの画像をという意味でPython NumPyとするために行った変更の一覧です。 png_bytep *datap;

  1. rTuple構造体では、あなたが使用してタイプpng_byteのポインタにpng_bytep datapを変更する必要があります。
  2. read_png_fileには、画像を読み取った後にフィラーバイトを追加するのにpng_set_fillerを使用してください。詳細については、hereを参照してください。 read_png_file

    if(color_type == PNG_COLOR_TYPE_RGB || 
        color_type == PNG_COLOR_TYPE_GRAY || 
        color_type == PNG_COLOR_TYPE_PALETTE) 
    png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER); 
    
  3. read_png_fileで、再びpng_read_update_info(png_ptr, info_ptr);

  4. を使用してrow_pointersを割り当てる前に、変更を更新、あなたが使用して画像ピクセルのためのメモリをmallocingされている方法に変更:

    png_bytep *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height); 
    for(row = 0; row < height; row++) 
    { 
        row_pointers[row] = malloc(png_get_rowbytes(png_ptr,info_ptr)); 
    } 
    
  5. mainの場合、ここにポインタとしてアクセスするとrow = &data.datap[i];からrow = data.datap[i];に変更してください。

私はやっと疑問と同じであるコードで答えを移入したくなかったので、あなただけの答えをコピー&ペーストしたい場合は、これはcomplete codeへのリンクです。

+0

素晴らしいです。あなたのすべての点をありがとう!それは理にかなっている。また、先日私は 'malloc'をキャストする必要はないと読んでいましたか?前に '(png_bytep *)'を削除すると、コンパイルに成功します。 – bjd2385

+1

よろしくお願いします!はい、 'malloc'のキャストは必要ありません。私はコードから削除しました。答えを改善していただきありがとうございます。 – hmofrad

関連する問題