Sunday, May 10, 2009

飛蛾

今天晚上突然出現一大群的飛蛾在家裡的窗戶外面,
突然來了一大群我嚇到了!

把窗戶關了起來,但是沒想到它還是想辦法鑽進來,
為了就是接近黑暗中耀眼的燈光。

當它們在接近光明的同時,
它們的生命也一步一步走向死亡..

Saturday, May 9, 2009

Using Jpeglib to Convert BMP to Jpeg

繼上一篇Using Jpeglib to Convert JPEG to BMP之後,我們想利用Jpeglib來將BMP轉成Jpeg。轉換格式其實最最重要的就是要讀對標頭檔(Header)也要填對它,所以之前所提的BMP Header是很重要的一環,在之前的文章也提到過了

利用jpeglib做jpeg壓縮的流程大致上與解壓縮是一樣的,重要的是要填對jpeg compression parameters:

int BmptoJpeg(char *input, char *output)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *out;
unsigned char *line, *buffer;
int row_width;
int i, j;

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

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);

jpeg_set_defaults(&cinfo);
jpeg_stdio_dest(&cinfo, out);
jpeg_set_quality(&cinfo, 75, TRUE);
jpeg_start_compress(&cinfo, TRUE);

/* set row width and calulate padding data*/
row_width = cinfo.image_width*cinfo.input_components;
/* !!!assume the input source store in buffer!!! */
line = buffer;

/* output to jpeg file */
for (i = 0; i < cinfo.image_height; i++, line+=row_width)
jpeg_write_scanlines(&cinfo, &line, 1);

jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);

fclose(in);
fclose(out);

return TRUE;
}


上列的code大致上就是整個jpeglib產生jpeg的流程,要特別注意的是buffer,在上面這個例子是假定buffer是存著圖的RGB資料(RAW Data)。了解整個產生jpeg流程之後,我們要來著手在bmp header的處理部份,之前提過的bmp header架構這裡也就不再提了。這邊要做的就是把bmp header裡的寬、高等資訊讀出,並且設定到jpeg compression的parameters:


int getBmpHeader(bmp_header *header, struct jpeg_compress_struct *info, FILE *in)
{
fread(header, 1, sizeof(bmp_header), in);

if (header->bmpType != 0x4d42 ) {
printf("getBmpHeader: ...it's not a BMP file\n");
return FALSE;
}

if (header->bmpCompress != 0 || header->bmpBpp != 24) {
printf("getBmpHeader: ...doesn't support compressed BMP format\n");
return FALSE;
}

info->image_width = header->bmpWidth;
info->image_height = (header->bmpHeight < 0)?
0-header->bmpHeight:header->bmpHeight;
info->input_components = header->bmpBpp/8;
info->in_color_space = JCS_RGB;

return TRUE;
}


設定之後其實也還有一項很重要的方不要忘了,就是bmp裡圖檔資料的格式是BGRBGR,而我們要輸入給jpeglib的資料是RGBRGB,所以color format轉換又要出現了:


int convertColor(unsigned 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;
}
}


這些都做完之後,我們就可以將jpeglib產生jpeg的流程做結合,做結合要注意的地方就是jpeglib產生jpeg時是採用一個line一個line做讀取,我們使用的做法是先將bmp的圖檔資料讀到memory中,再從memory一行一行的取出並做color format轉換再寫入到jpeg檔。另外還有一點要注意就是bmp是要對齊4bytes/row,所以我們也要試先算出多餘的資料,在每個line做累加的時候把這些多餘的資料捨去:


int BmptoJpeg(char *input, char *output)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
bmp_header binfo;
FILE *in, *out;
unsigned char *line, *buffer;
int row_width;
int i, j;

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

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

cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);

/* get bmp header info and set jpeg
compression parameters */
if (getBmpHeader(&binfo, &cinfo, in) == FALSE)
return FALSE;

jpeg_set_defaults(&cinfo);
jpeg_stdio_dest(&cinfo, out);
jpeg_set_quality(&cinfo, 75, TRUE);
jpeg_start_compress(&cinfo, TRUE);

/* set row width and calulate padding data*/
row_width = cinfo.image_width*cinfo.input_components
+ cinfo.image_width%4;
/* memory allocate for input buffer */
buffer = (unsigned char *)malloc(cinfo.image_width*cinfo.image_height*cinfo.input_components);
/* input from bmp file */
fread(buffer, 1, cinfo.image_width*
cinfo.image_height*cinfo.input_components, in);
line = buffer;

/* output to jpeg file */
for (i = 0; i < cinfo.image_height; i++, line+=row_width) {
convertColor(line, cinfo.image_width);
jpeg_write_scanlines(&cinfo, &line, 1);
}

jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);

fclose(in);
fclose(out);

free(buffer);

return TRUE;
}


上列的code其實還不算完整,這是對於沒有做壓縮(且沒有platte)的BMP是有效的,加上這個code並未做倒置的動作(BMP的圖檔資料是從左下到右上的順序做讀取),所以這個code還不算完整

Reference:
Wiki: BMP file format
IJG jpeglib

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

Sunday, May 3, 2009

Friendship

友誼真的是一件很奇妙的東西,
很久不見的朋友會很想知道他們最近在做什麼。
但是一旦熱絡起來之後,又變回當初認識的那個樣子,
想回到自己築好的保護巢裡..

『君子之交淡如水』有點這種感覺吧!
最要好的朋友卻不是在身邊的朋友,當朋友知道太多自己的事之後,
便會有衝突出現,人還是要有一點私人的空間,
這樣人和人的相處才會長長久久。