Need help in reading JPEG file using libjpeg
Solution 1
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.
Solution 2
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
}
Solution 3
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?
Solution 4
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( 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;
}
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.
Related videos on Youtube
all_by_grace
Software engineering technologist with 8+ years of software development experience (both at commercial industries and academics/research institutions), a proven record of team collaboration, agile development practices, excellent analytical skills, and solid expertise in distributed computing, data structure & algorithm, and object oriented software development & design. Have worked with top-notch supercomputers and cloud computing infrastructure during previous and current development roles at IBM Research, BGL Corporate Solutions, and other top industries. Inspired by the continuous innovations in distributed systems, cloud, and big data technology. Excellent analytical skills, self motivated, dedicated, adapt well to diverse teams and projects. M.S. in Computer Science & professionally certified in Linux & Scrum.
Updated on July 09, 2022Comments
-
all_by_grace almost 2 years
I followed the example code in the libjpeg example file, however I was not able to read the image data.
I have the following struct, and I created an instance of this struct.
struct ImageData { unsigned char *pixels; long width; long height; }; ImageData *imageData;
Below is my read_JPEG_file function:
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; }
It always fails on this JPEG_READ_SCANLINES function, in the step 6 above. I got an "EXC_BAD_ACCESS" signal on that line.
Does anyone have any idea, or have some working examples on reading .jpg file with libjpeg that you can share here? I have checked the size of my imageData->pixels, and compared it with the size of the jpeg file itself, and it has the same size. The memory for this variable has also dynamically allocated, so I know that it was not a memory problem.
Any ideas?
-
all_by_grace about 13 yearsYeah, I forgot to do the memcpy. I thought it is possible to use jpeg_read_scanlines to scan directly to imageData->pixels. I also tried something like: jpeg_read_scanlines(&cinfo, imageData->pixels+counter, 1), and increment the counter, but it doesn't work.
-
dacap about 13 yearsYes, you must use "buffer" in jpeg_read_scanlines(). Anyway I was thinking and you could put "buffer[0] = imageData->pixels" to avoid creating two rows of row_stride size and to avoid memcpy().
-
Camille Goudeseune almost 9 yearsFYI, the extern "C" is already in Ubuntu's current /usr/include/jpeglib.h, from package libjpeg62-dev.
-
Aubin almost 7 yearsif type = GL_RGBA, may data_size be x * y * 4 ?
-
Ruslan about 3 years
if(channels == 4) type = GL_RGBA;
, and then you don't usetype
anymore. Also,unsigned char * rowptr[1]; // pointer to an array
— the way it's declared, it's an array of pointers. -
Ruslan about 3 yearsCan't CImg read JPEG itself (via corresponding library bindings)?
-
Cris Luengo almost 3 yearsThis code misses a call to
jpeg_destroy_decompress
. -
M Katz almost 3 years@Chris Luengo Fixed.