2017-01-02 115 views
1

我試圖計算RGB三色空間與libpng在C,和NumPy在Python中的平均向量,但我得到不同的結果與每個。我非常有信心Python給出this image[ 127.5 127.5 0. ]的正確結果。但是,在下面的C塊中,我得到了[ 38.406494 38.433670 38.459641 ]的荒謬結果。我一直盯着我的代碼數週沒有任何給予,所以我想我會看看別人是否有想法。不同的結果與libpng和numpy的平均向量計算

此外,我已經測試了這個代碼與其他圖像,它給出了類似的荒謬結果。這很好奇,因爲所有三個數字通常匹配前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

基本上,你有幾個錯誤(libpng方面和指針算術),我試圖通過比較你的代碼與這Github gist找到他們。以下是我製作的相同圖片的變化列表,其中Python NumPy

  1. rTuple結構,您需要將png_bytep datap更改爲使用型png_byte的指針:png_bytep *datap;
  2. read_png_file中,使用png_set_filler在讀取圖像後添加填充字節。有關詳情,請參閱here

    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. 再次,在read_png_file更新分配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