【问题标题】:Need help in reading JPEG file using libjpeg在使用 libjpeg 读取 JPEG 文件时需要帮助
【发布时间】:2011-04-11 02:26:10
【问题描述】:

我遵循了 libjpeg 示例文件中的示例代码,但是我无法读取图像数据。

我有以下结构,我创建了这个结构的一个实例。

 struct ImageData {
        unsigned char *pixels;
        long  width;
        long height;
    };

    ImageData *imageData;

下面是我的 read_JPEG_file 函数:

int read_JPEG_file (char * filename)
{
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;

    /* More stuff */
    FILE * infile;      /* source file */
    JSAMPARRAY buffer;      /* Output row buffer */
    int row_stride;     /* physical row width in output buffer */

    if ((infile = fopen(filename, "rb")) == NULL) {
        fprintf(stderr, "can't open %s\n", filename);
        return 0;
    }

    /* Step 1: allocate and initialize JPEG decompression object */

    /* We set up the normal JPEG error routines, then override error_exit. */
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerr.setjmp_buffer)) {

        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 0;
    }
    /* Now we can initialize the JPEG decompression object. */
    jpeg_create_decompress(&cinfo);

    /* Step 2: specify data source (eg, a file) */

    jpeg_stdio_src(&cinfo, infile);

    /* Step 3: read file parameters with jpeg_read_header() */

    (void) jpeg_read_header(&cinfo, TRUE);
    /* Step 4: set parameters for decompression */

    /* In this example, we don't need to change any of the defaults set by
     * jpeg_read_header(), so we do nothing here.
     */

    /* Step 5: Start decompressor */

    (void) jpeg_start_decompress(&cinfo);


    row_stride = cinfo.output_width * cinfo.output_components;
    /* Make a one-row-high sample array that will go away when done with image */
    buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

    imageData = new ImageData;
    imageData->width = cinfo.output_width;
    imageData->height = cinfo.output_height;

    imageData->pixels = new unsigned char [cinfo.output_width * cinfo.output_height * cinfo.output_components];
    long counter = 0;

   //step 6, read the image line by line
    while (cinfo.output_scanline < cinfo.output_height) {
        //IT ALWAYS crash ON THIS JPEG_READ_SCANLINES FUNCTION CALL BELOW
        (void) jpeg_read_scanlines(&cinfo, (JSAMPARRAY)(imageData->pixels), 1);
        counter +=row_stride;

    }
       /* Step 7: Finish decompression */

    (void) jpeg_finish_decompress(&cinfo);
    /* Step 8: Release JPEG decompression object */

    /* This is an important step since it will release a good deal of memory. */
    jpeg_destroy_decompress(&cinfo);

    fclose(infile);
    /* And we're done! */
    return 1;
}

在上面的第 6 步中,这个 JPEG_READ_SCANLINES 函数总是失败。我在那条线上收到了“EXC_BAD_ACCESS”信号。

有没有人有任何想法,或者有一些关于使用 libjpeg 读取 .jpg 文件的工作示例,您可以在这里分享吗?我检查了我的 imageData->pixels 的大小,并将其与 jpeg 文件本身的大小进行了比较,它具有相同的大小。这个变量的内存也是动态分配的,所以我知道这不是内存问题。

有什么想法吗?

【问题讨论】:

    标签: c++ libjpeg


    【解决方案1】:

    这是一个读取 jpeg 图像的示例:

    /***************************************************
        To read a jpg image file and download
        it as a texture map for openGL
        Derived from Tom Lane's example.c
        -- Obtain & install jpeg stuff from web 
        (jpeglib.h, jerror.h jmore.h, jconfig.h,jpeg.lib)
    ****************************************************/
    #include <jpeglib.h>    
    #include <jerror.h>
    //================================
    GLuint LoadJPEG(char* FileName)
    //================================
    {
      unsigned long x, y;
      unsigned int texture_id;
      unsigned long data_size;     // length of the file
      int channels;               //  3 =>RGB   4 =>RGBA 
      unsigned int type;  
      unsigned char * rowptr[1];    // pointer to an array
      unsigned char * jdata;        // data for the image
      struct jpeg_decompress_struct info; //for our jpeg info
      struct jpeg_error_mgr err;          //the error handler
    
      FILE* file = fopen(FileName, "rb");  //open the file
    
      info.err = jpeg_std_error(& err);     
      jpeg_create_decompress(& info);   //fills info structure
    
      //if the jpeg file doesn't load
      if(!file) {
         fprintf(stderr, "Error reading JPEG file %s!", FileName);
         return 0;
      }
    
      jpeg_stdio_src(&info, file);    
      jpeg_read_header(&info, TRUE);   // read jpeg file header
    
      jpeg_start_decompress(&info);    // decompress the file
    
      //set width and height
      x = info.output_width;
      y = info.output_height;
      channels = info.num_components;
      type = GL_RGB;
      if(channels == 4) type = GL_RGBA;
    
      data_size = x * y * 3;
    
      //--------------------------------------------
      // read scanlines one at a time & put bytes 
      //    in jdata[] array. Assumes an RGB image
      //--------------------------------------------
      jdata = (unsigned char *)malloc(data_size);
      while (info.output_scanline < info.output_height) // loop
      {
        // Enable jpeg_read_scanlines() to fill our jdata array
        rowptr[0] = (unsigned char *)jdata +  // secret to method
                3* info.output_width * info.output_scanline; 
    
        jpeg_read_scanlines(&info, rowptr, 1);
      }
      //---------------------------------------------------
    
      jpeg_finish_decompress(&info);   //finish decompressing
    
      //----- create OpenGL tex map (omit if not needed) --------
      glGenTextures(1,&texture_id);
      glBindTexture(GL_TEXTURE_2D, texture_id);
      gluBuild2DMipmaps(GL_TEXTURE_2D,3,x,y,GL_RGB,GL_UNSIGNED_BYTE,jdata);
    
      jpeg_destroy_decompress(&info);
      fclose(file);                    //close the file
      free(jdata);
    
      return texture_id;    // for OpenGL tex maps
    }
    

    【讨论】:

    • 如果 type = GL_RGBA,data_size 可能是 x * y * 4 吗?
    • if(channels == 4) type = GL_RGBA;,然后你就不再使用type了。另外,unsigned char * rowptr[1]; // pointer to an array — 它的声明方式是一个指针数组。
    【解决方案2】:

    jpeg_read_scanlines 函数接收一个指针数组(不是像 imageData->pixels 那样的像素直接指针)。所以我们应该先创建一个 JSAMPARRAY:

    int buffer_height = 1;
    JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW) * buffer_height);
    buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_stride);
    

    在您的代码中,您使用“cinfo.mem->alloc_sarray”创建了一个“缓冲区”,但您从未使用它。最后一步是将“缓冲区”作为 jpeg_read_scanlines 的参数传递:

    while (cinfo.output_scanline < cinfo.output_height) {
      jpeg_read_scanlines(&cinfo, buffer, 1);
      memcpy(imageData->pixels+counter, buffer[0], row_stride);
      counter += row_stride;
    }
    

    看到我们使用的是“imageData->pixels+counter”,而不仅仅是代码中的“imageData->pixels”。通过这种方式,我们在整个“imageData->pixels”内存块中逐行写入。

    【讨论】:

    • 是的,我忘了做 memcpy。我认为可以使用 jpeg_read_scanlines 直接扫描到 imageData->pixels。我也尝试过类似的方法:jpeg_read_scanlines(&cinfo, imageData->pixels+counter, 1),并增加计数器,但它不起作用。
    • 是的,您必须在 jpeg_read_scanlines() 中使用“缓冲区”。无论如何,我在想,你可以放“buffer[0] = imageData->pixels”来避免创建两行row_stride大小并避免memcpy()。
    【解决方案3】:

    就像 dacap 所说,它期待一个 JSAMPARRAY。话虽如此,如果您愿意,您可以直接写入您的 imageData->pixels 数组。你只需要这样做:

    // Allocate imageData->pixels to be the correct size, start decompress and all
    // that jazz, like you did in your code. Skip allocating buffer though.
    // ...
    
    JSAMPROW output_data;
    unsigned int scanline_len = cinfo.output_width * cinfo.output_components;
    
    unsigned int scanline_count = 0;
    while (cinfo.output_scanline < cinfo.output_height)
    {
        output_data = (imageData->pixels + (scanline_count * scanline_len));
        jpeg_read_scanlines(&cinfo, &output_data, 1);
        scanline_count++;
    }
    

    然后您可以完全跳过分配缓冲区。使用 memcpy 效果很好,但如果不需要,为什么还要额外复制呢?

    【讨论】:

      【解决方案4】:

      这是由 Jim 整理的代码版本。它还使用CImg 作为输出格式,这使得提取 RGB 值的方式更加明确。

      CImg<unsigned char> *ReadJpegIntoCImg( const char *path )
      {
          FILE *file = fopen( path, "rb" );
          if ( file == NULL )
          {
              return NULL;
          }
          
          struct jpeg_decompress_struct info; //for our jpeg info
          struct jpeg_error_mgr err; //the error handler
          
          info.err = jpeg_std_error( &err );     
          jpeg_create_decompress( &info ); //fills info structure
          
          jpeg_stdio_src( &info, file );    
          jpeg_read_header( &info, true );
          
          jpeg_start_decompress( &info );
          
          unsigned int w = info.output_width;
          unsigned int h = info.output_height;
          unsigned int numChannels = info.num_components; // 3 = RGB, 4 = RGBA
          unsigned long dataSize = w * h * numChannels;
          
          // read RGB(A) scanlines one at a time into jdata[]
          unsigned char *data = (unsigned char *)malloc( dataSize );
          unsigned char* rowptr;
          while ( info.output_scanline < h )
          {
              rowptr = data + info.output_scanline * w * numChannels;
              jpeg_read_scanlines( &info, &rowptr, 1 );
          }
          
          jpeg_finish_decompress( &info );
          jpeg_destroy_decompress( &info );   
          
          fclose( file );
          
          // this code block is only if you happen to want output in CImg format, but is illustrative
          CImg<unsigned char> *image = new CImg<unsigned char>( w, h, 1, numChannels );
          for ( int x = 0 ; x < w ; x++ )
          {
              for ( int y = 0 ; y < h ; y++ )
              {
                  for ( int c = 0 ; c < numChannels ; c++ )
                  {
                      *image->data( x, y, 0, c ) = data[ y * w * numChannels + x * numChannels + c ];
                  }
              }
          }
          
          free( data );
          
          return image;
      }
      

      另外,对于那些可能遇到与我相同的问题的人来说,在使用 C++ 中的 libjpeg 时,拥有一个包含以下内容的 jpeglib.h 版本很重要

      #ifdef __cplusplus
      extern "C" {
      #endif
      

      我使用的文件版本没有该文件,并且出现链接错误。

      【讨论】:

      • 仅供参考,外部“C”已经在 Ubuntu 当前的 /usr/include/jpeglib.h 中,来自包 libjpeg62-dev。
      • CImg 不能读取 JPEG 本身(通过相应的库绑定)?
      • 此代码错过了对jpeg_destroy_decompress的调用。
      • @Chris Luengo 已修复。
      猜你喜欢
      • 2013-11-25
      • 1970-01-01
      • 1970-01-01
      • 2019-10-12
      • 2013-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      相关资源
      最近更新 更多