Friday, January 16, 2009

C Code to convert JPEG to BMP in LINUX using libjpeg

Intro
The following code converts JPEG file into BMP file. The code requires the libjpeg library. The following code is a good example of how to use the libjpeg library. (it compiles).
Code converts colour jpeg to 24bit BITMAP.

Code : jpeg2bmp.c

#include <stdio.h>
#include <jpeglib.h>
#include <stdlib.h>
#include <string.h>

/* we will be using this uninitialized pointer later to store raw, uncompressd image */
unsigned char *raw_image = NULL;

/* dimensions of the image we want to write */
int width;
int height;
int bytes_per_pixel; /* or 1 for GRACYSCALE images */
int color_space; /* or JCS_GRAYSCALE for grayscale images */

typedef struct {
long filesize;
char reserved[2];
long headersize;
long infoSize;
long width;
long depth;
short biPlanes;
short bits;
long biCompression;
long biSizeImage;
long biXPelsPerMeter;
long biYPelsPerMeter;
long biClrUsed;
long biClrImportant;
} BMPHEAD;

int write_bmp_file( char *filename )
{

BMPHEAD bh;

memset ((char *)&bh,0,sizeof(BMPHEAD)); /* sets everything to 0 */

//bh.filesize = calculated size of your file (see below)
//bh.reserved = two zero bytes
bh.headersize = 54L;// (for 24 bit images)
bh.infoSize = 0x28L;// (for 24 bit images)
bh.width = width ;//in pixels of your image
bh.depth = height;// in pixels of your image
bh.biPlanes = 1 ;//(for 24 bit images)
bh.bits = 24 ;//(for 24 bit images)
bh.biCompression = 0L;;// (no compression)

int bytesPerLine;

bytesPerLine = width * 3; /* (for 24 bit images) */
/* round up to a dword boundary */
if (bytesPerLine & 0x0003)
{
bytesPerLine |= 0x0003;
++bytesPerLine;
}
bh.filesize=bh.headersize+(long)bytesPerLine*bh.depth;

FILE * bmpfile;

printf("Bytes per line : %d\n", bytesPerLine);

bmpfile = fopen(filename, "wb");
if (bmpfile == NULL)
{
printf("Error opening output file\n");
/* -- close all open files and free any allocated memory -- */
exit (1);
}
fwrite("BM",1,2,bmpfile);
fwrite((char *)&bh, 1, sizeof (bh), bmpfile);

char *linebuf;

linebuf = (char *) calloc(1, bytesPerLine);
if (linebuf == NULL)
{
printf ("Error allocating memory\n");
free(raw_image);
/* -- close all open files and free any allocated memory -- */
exit (1);
}


int line,x;

for (line = height-1; line >= 0; line --)
{
/* fill line linebuf with the image data for that line */
for( x =0 ; x < width; x++ )
{
*(linebuf+x*bytes_per_pixel) = *(raw_image+(x+line*width)*bytes_per_pixel+2);
*(linebuf+x*bytes_per_pixel+1) = *(raw_image+(x+line*width)*bytes_per_pixel+1);
*(linebuf+x*bytes_per_pixel+2) = *(raw_image+(x+line*width)*bytes_per_pixel+0);
}

/* remember that the order is BGR and if width is not a multiple
of 4 then the last few bytes may be unused
*/
fwrite(linebuf, 1, bytesPerLine, bmpfile);
}
free(linebuf);
fclose(bmpfile);



}



/**
* read_jpeg_file Reads from a jpeg file on disk specified by filename and saves into the
* raw_image buffer in an uncompressed format.
*
* \returns positive integer if successful, -1 otherwise
* \param *filename char string specifying the file name to read from
*
*/

int read_jpeg_file( char *filename )
{
/* these are standard libjpeg structures for reading(decompression) */
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
/* libjpeg data structure for storing one row, that is, scanline of an image */
JSAMPROW row_pointer[1];

FILE *infile = fopen( filename, "rb" );
unsigned long location = 0;
int i = 0;

if ( !infile )
{
printf("Error opening jpeg file %s\n!", filename );
return -1;
}
/* here we set up the standard libjpeg error handler */
cinfo.err = jpeg_std_error( &jerr );
/* setup decompression process and source, then read JPEG header */
jpeg_create_decompress( &cinfo );
/* this makes the library read from infile */
jpeg_stdio_src( &cinfo, infile );
/* reading the image header which contains image information */
jpeg_read_header( &cinfo, TRUE );
/* Uncomment the following to output image information, if needed. */

printf( "JPEG File Information: \n" );
printf( "Image width and height: %d pixels and %d pixels.\n", width=cinfo.image_width, height=cinfo.image_height );
printf( "Color components per pixel: %d.\n", bytes_per_pixel = cinfo.num_components );
printf( "Color space: %d.\n", cinfo.jpeg_color_space );

/* Start decompression jpeg here */
jpeg_start_decompress( &cinfo );

/* allocate memory to hold the uncompressed image */
raw_image = (unsigned char*)malloc( cinfo.output_width*cinfo.output_height*cinfo.num_components );
/* now actually read the jpeg into the raw buffer */
row_pointer[0] = (unsigned char *)malloc( cinfo.output_width*cinfo.num_components );
/* read one scan line at a time */
while( cinfo.output_scanline < cinfo.image_height )
{
jpeg_read_scanlines( &cinfo, row_pointer, 1 );
for( i=0; i<cinfo.image_width*cinfo.num_components;i++)
raw_image[location++] = row_pointer[0][i];
}
/* wrap up decompression, destroy objects, free pointers and close open files */
jpeg_finish_decompress( &cinfo );
jpeg_destroy_decompress( &cinfo );
free( row_pointer[0] );
fclose( infile );
/* yup, we succeeded! */
return 1;
}


int main(int argc,char **argv)
{
int x,y;

if(argc != 3){ printf("Usage: %s source.jpg dest.bmp",argv[0]); return -1; }
x=y=0;
/* Try opening a jpeg*/
if( read_jpeg_file( argv[1] ) > 0 ) write_bmp_file( argv[2] );

else return -1;

free(raw_image);
return 0;
}


Compiling
gcc jpeg2bmp.c  -ljpeg  -o jpeg2bmp


Usage
jpeg2bmp source.jpg destination.bmp 


...
Tested on ubuntu 8.1.(64bit)
Any problems ? Let me know...

18 comments:

Punnoose said...

Da your code doesn't work on DevCPP for Windows. The error shown is.
[Linker error] undefined reference to `_imp__jpeg_start_decompress'


I have installed the Dev version of LibJPEG but it won't work.. Please tell me how I can port it to Windows... OR can you compile and bring the EXE today

安宁 said...

why use -ljpg?
i can't google the information about it~
thanks

Arun Prabhakar said...

to load the JPEG libraries

-ljpeg

安宁 said...

THANKS
but in the code
#include jpeglib.h
it has loaded the JPEG libraries?
and it can be ran with WINDOWS?

Arun Prabhakar said...

lpeg is for the linker to load the object files when linking.

It should work for windows, if you find the appropriate libraries for libjpeg for windows,

(not just the header files)

安宁 said...

ok
how can i debug the code dans IDE?
for example CODE::BLOCKS
because undefined reference to `jpeg_std_error'
`jpeg_CreateDecompress'
etc
thank you for your help
:)

Arun Prabhakar said...

dans IDE ?
I dint get that
I havn't tried this on Windows :)
It works fine in Linux.

安宁 said...

I'm in French sometimes I mix English and French I'm sorry for that

now I'm tring it on linux and It works fine in Linux
(gcc jpeg2bmp.c -ljpeg -o jpeg2bmp jpeg2bmp source.jpg destination.bmp )

but when I use the IDE(CODE::BLOCKS) to debug it

undefined reference to `jpeg_std_error'|
undefined reference to `jpeg_std_error'|
undefined reference to `jpeg_CreateDecompress'|
undefined reference to `jpeg_stdio_src'|
undefined reference to `jpeg_read_header'|
undefined reference to `jpeg_start_decompress'|
undefined reference to `jpeg_read_scanlines'|
undefined reference to `jpeg_finish_decompress'|
undefined reference to `jpeg_destroy_decompress'|

I know these problems about loading the JPEG libraries but I don't get the way to solve them on IDE

thank you

Arun Prabhakar said...

pls check if there is a libjpeg library for your compiler on windows

Anonymous said...

i compiled the program on linux and it works great, but when i deploy to gumstix, i get the following error

jpeg2bmp Obama.jpg Obama.bmp
jpeg2bmp: jpeg2bmp: 1: Syntax error: "(" unexpected

Rahul Ghose said...

Hey, thanks a ton for your code!

I used it in a steganographic project.

rsaxvc said...

This code leaks RAM. The buffer in write_bmp_file is never freed.

Francia said...

Hi, this code not work with 8bit-jpeg.. How could I solve this?

Arun Prabhakar said...

Try changing the number of bytes per line from 24 to 8 ?

Anonymous said...

Just Thanks! Passed a lot of time ajusting the code for my needs, but it did the trick!! :) tks

Craig said...

Thanks for the code! I'm having a problem opening the output image. I've tried every program I can think of (Linux and Windows). What am I missing?

Craig said...

Well I figured it out. If anyone has a problem opening the output file, It's probably due to the header of the output file. I had to construct it my self. But this will only work for 640x480 images. The code will need to be tweaked for other resolutions.

typedef struct {
long filesize;
long headersize;
short infoSize;
long width;
long depth;
short biPlanes;
short bits;
long biCompression;
short biSizeImage;
long datasize;
long Hres;
long Vres;
long number_colors;
long important_colors;
}BMPHEAD;

int write_bmp_file( char *filename )
{

BMPHEAD bh;

memset ((char *)&bh,0,sizeof(BMPHEAD)); /* sets everything to 0 */

bh.headersize = 54;// (for 24 bit images)
bh.infoSize = 0x28;// (for 24 bit images)
bh.width = width ;//in pixels of your image
bh.depth = height;// in pixels of your image
bh.biPlanes = 1 ;//(for 24 bit images)
bh.bits = 24 ;//(for 24 bit images)
bh.biCompression = 0L;// (no compression)
bh.datasize = 0x000E1000;
bh.Hres = 0x00000B13;
bh.Vres = 0x00000B13;
bh.number_colors = 0x00000000;
bh.important_colors = 0x00000000;

int bytesPerLine;

bytesPerLine = width * 3; /* (for 24 bit images) */
/* round up to a dword boundary */
if (bytesPerLine & 0x0003)
{
bytesPerLine |= 0x0003;
++bytesPerLine;
}
bh.filesize=bh.headersize+(long)bytesPerLine*bh.depth;

FILE * bmpfile;



bmpfile = fopen(filename, "wb");
if (bmpfile == NULL)
{
printf("Error opening output file\n");
/* -- close all open files and free any allocated memory -- */
exit (1);
}
fwrite("BM",1,2,bmpfile);

fwrite(&bh, 1,8, bmpfile); //needed to start header

fwrite(&bh.headersize, 1,4, bmpfile);
fwrite(&bh.infoSize, 1,4, bmpfile);
fwrite(&bh.width, 1,4, bmpfile);
fwrite(&bh.depth, 1,4, bmpfile);
fwrite(&bh.biPlanes, 1,2, bmpfile);
fwrite(&bh.bits, 1,2, bmpfile);
fwrite(&bh.biCompression, 1,4, bmpfile);
fwrite(&bh.datasize, 1,4, bmpfile);
fwrite(&bh.Hres, 1,4, bmpfile);
fwrite(&bh.Vres, 1,4, bmpfile);
fwrite(&bh.number_colors, 1,4, bmpfile);
fwrite(&bh.important_colors, 1,4, bmpfile);

Anonymous said...

Hey Arun,

Thanks for the code, very helpful this morning when I had to get something done quickly. Just a quick note (not sure if you're maintaining this piece of code anymore), your bmp header contains a bunch of longs. I was running the code on my macbook, which is 64 bit, so the struct got compiled with 8 bytes allocated per long and the file result is unreadable.

I just did a quick and dirty find/replace long to unsigned int's, but the better way to handle it would be to just use the typedefs from stdint.h to make it more crossplatform friendly.

In any case, thanks for the helpful piece of code!