2017-01-02 64 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