Need help in reading JPEG file using libjpeg

萝らか妹 提交于 2019-11-28 21:59:00

jpeg_read_scanlines function receives an array of pointers (not the direct pointer of pixels as imageData->pixels). So we should create a JSAMPARRAY first:

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

In your code you've created a "buffer" with "cinfo.mem->alloc_sarray" but you never use it. The final step is to pass the "buffer" as argument of 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;
}

See that we're using "imageData->pixels+counter", not just "imageData->pixels" as in your code. In this way we write each row after another in the whole "imageData->pixels" memory chunk.

Jim

Here's a sample for reading a jpeg image:

/***************************************************
    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
}

Like dacap said, it is expecting a JSAMPARRAY. That being said, you can write straight to your imageData->pixels array if you would like. You just need to do something like this:

// 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++;
}

You can then skip allocating buffer altogether. Using memcpy works fine, but why do the extra copy if you don't have to?

M Katz

Here's a cleaned-up version of the code by Jim. It also uses a CImg as the output format, which makes it a bit more explicit how the RGB values are extracted.

CImg<unsigned char> *ReadJpegIntoCImg( 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 );

    int w = info.output_width;
    int h = info.output_height;
    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 );    

    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;
}

Also, a side note for those who may have been stuck on the same issue I was, when using libjpeg from C++, it's important to have a version of jpeglib.h that includes

#ifdef __cplusplus
extern "C" {
#endif

I was using a version of the file that didn't have that, and was getting link errors.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!