Friday, May 8, 2009

Using Jpeglib to Convert JPEG to BMP

下載Jpeglib

ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz


將Jpeglib解壓縮

tar -zxvf jpegscr.v6b.tar.gz


Build Share library

./configure --enable-shared
make


Build完之後會在.lib資料夾底下看到libjpeg.so的檔案,如果沒有這個檔案則表示沒有Build成Share Library或者是根本沒有Build成功。如果有這個檔案則我們可以開始作安裝的動作(預設安裝目錄是在/usr/local,如果要更改目錄則在一開始configure的時候加上--prefix=安裝目錄)

make install


接著就可以開始寫轉換的程式了!首先我們先了解Jpeglib如何將Jpeg檔解壓縮:


int JpegtoBmp(char *input)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *in;
JSAMPARRAY buffer;
int row_width;

if ( (in = fopen(input, "rb")) == NULL ) {
printf("readJPEG: ...can't open %s\n", input);
return FALSE;
}

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, in);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);

/* SAMPLEs per row in output buffer */
row_width = cinfo.output_width * cinfo.output_components;
/* set buffer */
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_width, 1);

while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
/* !!!output row data!!! */
}

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);

fclose(in);
return TRUE;
}


大致的流程就如上面的程式碼所看到的,比較要注意的就是struct jpeg_decompress_struct這個東西,當我們在做解壓縮的時候,這個struct裡面有一些參數可以設定解壓縮的一些模式、輸出的格式等等,詳細可以參考jpeglib裡面的libjpeg.doc這個說明檔

了解整個jpeglib的解壓縮流程之後接著我們要來看要如何產生一個BMP File,首先我們要先了解BMP的Header:




由上面所列的Header我們可以定義出一個BMP Header的Struct:

typedef struct
{
unsigned short bmpType;
unsigned long bmpSize;
unsigned long bmpRev;
unsigned long bmpOffset;
unsigned long bmpHeaderSize;
long bmpWidth;
long bmpHeight;
unsigned short bmpPlanes;
unsigned short bmpBpp;
unsigned long bmpCompress;
unsigned long bmpDataSize;
long bmpHPelsPerMeter;
long bmpVPelsPerMeter;
unsigned long bmpColors;
unsigned long bmpImportant;
}__attribute__ ((packed)) bmp_header;




接著我們設計一個function來產生一個BMP Header,這邊要注意一點,因為BMP讀的順序是從左下到右上如上圖所示,所以我們利用B設定BMP Header裡的高將高改成負的,這樣就可以讓順序改成從左上到右下讀取,這樣不需再做另外倒置的處理:


int setBmpHeader(bmp_header *header, struct jpeg_decompress_struct *info)
{
long size;
size = info->output_width*info->output_height*3;

header->bmpType = 0x4d42;
header->bmpSize = size + sizeof(bmp_header);
header->bmpRev = 0;
header->bmpOffset = header->bmpSize - size;
header->bmpHeaderSize = 40;
header->bmpWidth = info->output_width;
header->bmpHeight = 0-info->output_height;
header->bmpPlanes = 1;
header->bmpBpp = 24;
header->bmpCompress = 0;
header->bmpDataSize = size;
header->bmpHPelsPerMeter = 0;
header->bmpVPelsPerMeter = 0;
header->bmpColors = 0;
header->bmpImportant = 0;

}


我們透過struct jpeg_decompress_struct來取得原始jpeg檔的大小(寬、高)來對產生的BMP做Header的設定動作。因為BMP儲存的格式是BGRBGR 但是jpeglib解出來的jpeg color format是RGBRGB 所以要作轉換,我們需要一個color format轉換的function:


int convertColor(char *color, int width)
{
int i, ptr;
char tmp;

for(i = 0, ptr= 0; i < width; i++, ptr=i*3) {
tmp = color[ptr];
color[ptr] = color[ptr+2];
color[ptr+2] = tmp;
}
}


接著我們將jpeg最原始的解壓縮程式改寫,並將BMP Header設定與Color Format轉換加入,但是還要注意一點就是BMP的資料每列一定要對齊4bytes/row 所以在寫入資料的時候必須要注意是否有補滿4bytes/row,所以我們在寫入每一列時,我們必須先計算一列有多少個byte然後對它做除以4取餘數,看我們需要多少個0需要做填入的動作:


int JpegtoBmp(char *input, char *output)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
bmp_header binfo;
FILE *in, *out;
JSAMPARRAY buffer;
int row_width;
int num_padding;

if ( (in = fopen(input, "rb")) == NULL ) {
printf("readJPEG: ...can't open %s\n", input);
return FALSE;
}

if ( (out = fopen(output, "wb+")) == NULL ) {
printf("readJPEG: ...can't open %s\n", output);
return FALSE;
}

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, in);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
setBmpHeader(&binfo, &cinfo);

/* write BMP header */
fwrite(&binfo, 1, sizeof(bmp_header), out);
/* SAMPLEs per row in output buffer */
row_width = cinfo.output_width * cinfo.output_components;
/* calculate number of padding */
num_padding = cinfo.output_width%4;
/* set buffer */
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_width, 1);

while (cinfo.output_scanline < cinfo.output_height) {
jpeg_read_scanlines(&cinfo, buffer, 1);
convertColor(buffer[0], cinfo.output_width);
fwrite(buffer[0], 1, row_width, out);
fwrite("0", 1, num_padding, out);
}

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);

fclose(in);
fclose(out);
return TRUE;
}


Reference:
Wiki: BMP file format
IJG jpeglib

No comments:

Post a Comment