BMP文件的parse
时间:2009-03-29 来源:taoyuliang
因为考虑用硬件做一个JPEG的编码器,所以需要对原始的RGB图像数据进行处理。前段时间看了不少关于JPEG编解码和DCT变换的paper,软件的模型基本上已经建立起来,接下来的工作就是要在Soc Designer上进行系统的建模,并把图像编码部分做成一个硬件加速的IP核。话题偏离了,呵呵。所有这些工作都需要原始图像的RGB数据进行输入,所以昨天写了一个解析bmp文件的perl程序。
该perl程序接受一个参数,即需要parse的bmp文件名(目前只支持24-bit bmp格式),然后将会在屏幕上打印出bmp文件头信息,并将RGB数据保存在txt的文本文件里面。另外,因为在做DCT变换之前需要进行颜色空间的转换RGB->YCbCr,所以该程序中还进行了RGB到YCbCr的转换,并将YCbCr信息也输出到txt文件中。
其实我觉得这个程序用python来写的话或许会更好一点,不过我很喜欢perl的正则表达式。其中有几个问题是需要注意的:
1.对bmp文件读入后需要用binmode来转换成二进制格式,另外还需要用unpack函数来转换我们需要的格式。关于unpack函数,可以参考perlfunc或者google之;
2.在对bmp文件进行读入的时候,十六进制的"0A"会被认为是换行符'\n',所以如果RGB数据中存在"0A"数据,<>操作符会停止,所以有可能所有数据并未全部读入,需要在后面进行继续读入所有数据;
3.同样是"0A"字符带来的问题。我们一般对读入的每一行都采取一个chomp操作将末尾的换行符去掉,但是在bmp文件中,千万不能使用chomp函数,因为这里的"0A"并未真正的换行符,而是有效数据。我开始就是这样,不知道为什么,总是差一个数据,搞了很久才发现这个问题。
4.bmp文件中的RGB数据是按最后一行到第一行的顺序来存储的,所以将所有RGB数据读入后,需要进行顺序的调整,请参考bmp文件格式。
源代码如下:
该perl程序接受一个参数,即需要parse的bmp文件名(目前只支持24-bit bmp格式),然后将会在屏幕上打印出bmp文件头信息,并将RGB数据保存在txt的文本文件里面。另外,因为在做DCT变换之前需要进行颜色空间的转换RGB->YCbCr,所以该程序中还进行了RGB到YCbCr的转换,并将YCbCr信息也输出到txt文件中。
其实我觉得这个程序用python来写的话或许会更好一点,不过我很喜欢perl的正则表达式。其中有几个问题是需要注意的:
1.对bmp文件读入后需要用binmode来转换成二进制格式,另外还需要用unpack函数来转换我们需要的格式。关于unpack函数,可以参考perlfunc或者google之;
2.在对bmp文件进行读入的时候,十六进制的"0A"会被认为是换行符'\n',所以如果RGB数据中存在"0A"数据,<>操作符会停止,所以有可能所有数据并未全部读入,需要在后面进行继续读入所有数据;
3.同样是"0A"字符带来的问题。我们一般对读入的每一行都采取一个chomp操作将末尾的换行符去掉,但是在bmp文件中,千万不能使用chomp函数,因为这里的"0A"并未真正的换行符,而是有效数据。我开始就是这样,不知道为什么,总是差一个数据,搞了很久才发现这个问题。
4.bmp文件中的RGB数据是按最后一行到第一行的顺序来存储的,所以将所有RGB数据读入后,需要进行顺序的调整,请参考bmp文件格式。
源代码如下:
#!/usr/bin/perl -w # --------------------------------------------------------- # parse bmp file to get RGB information # by Tao Yuliang <[email protected]> # 28/03/2009 # --------------------------------------------------------- # #use strict; # prepare my @bmp_tag = qw( bfType bfSize bfReserved1 bfReserved2 bfOffBits biSize biWidth biHeight biPlanes biBitCount biCompression biSizeImage biXPelsPerMeter biYPelsPerMeter biClrUsed biClrImportant ); binmode STDIN; binmode STDOUT; my $data_format = "H"; #------------------------------------------# # read the bmp file and parse the data # #------------------------------------------# # get the file name die "No input file!\n" if not $filename = shift @ARGV; # open the file open RGBFILE, $filename or die "Can't open $filename: $!"; binmode RGBFILE; # read the bmp file my $data = <RGBFILE> ; # convert the data into a certain format # to get the information of bmp file #my @hdr_dat = unpack "SLSSLLLLSSLLLLLLC*", $data; my @hdr_dat = unpack "SLSSLLLLSSLLLLLL$data_format*", $data; $hdr_dat[0] = join '', unpack "aa", $data; #print "@hdr_dat\n"; # get the image data form $hdr_dat[-1] my @rgb_data; for ( 1..($#hdr_dat-$#bmp_tag) ) { unshift @rgb_data, pop @hdr_dat; } #print "@hdr_dat\n"; #print "@rgb_data\n"; # if there is '0A' data in image data, perl will treate it as a '\n' # so we must read the remaining data while ( <RGBFILE> ) { # don't chomp, else it will discards '0A' #chomp ; push @rgb_data, unpack "$data_format*", $_; } close RGBFILE; # store the info of file structure into a hash my %header; @header{@bmp_tag}=@hdr_dat; print "$_\t$header{$_}\n" for @bmp_tag; #------------------------------------------# # get the RGB info and write into txt file # #------------------------------------------# my @table_R; my @table_G; my @table_B; if ( $data_format eq 'H' ) { &process_H_format ; } else { &process_C_format ; } open INFOTXT, "> $filename.txt" or die "Can't open $filename.txt: $!"; print INFOTXT "R\n"; print INFOTXT "@table_R\n"; print INFOTXT "G\n"; print INFOTXT "@table_G\n"; print INFOTXT "B\n"; print INFOTXT "@table_B\n"; #------------------------------------------# # convert RGB to YCbCr # #------------------------------------------# my ( @YR_LUT, @CbR_LUT, @CrR_LUT ); my ( @YG_LUT, @CbG_LUT, @CrG_LUT ); my ( @YB_LUT, @CbB_LUT, @CrB_LUT ); # call subroutine &prepare_Y_LUT ; &prepare_Cb_LUT ; &prepare_Cr_LUT ; # get the YCbCr data according to LUT my ( @table_Y, @table_Cb, @table_Cr ); for ( 0..$#table_R ) { my $R = ( $data_format eq "H" ) ? hex( $table_R[$_] ) : $table_R[$_]; my $G = ( $data_format eq "H" ) ? hex( $table_G[$_] ) : $table_G[$_]; my $B = ( $data_format eq "H" ) ? hex( $table_B[$_] ) : $table_B[$_]; push @table_Y, int( ($YR_LUT[$R]+$YG_LUT[$G]+$YB_LUT[$B]) / 2**16 ); push @table_Cb, ( int(($CbR_LUT[$R]+$CbG_LUT[$G]+$CbB_LUT[$B]) / 2**16) + 128 ); push @table_Cr, ( int(($CrR_LUT[$R]+$CrG_LUT[$G]+$CrB_LUT[$B]) / 2**16) + 128 ); } # write into file print INFOTXT "Y\n"; print INFOTXT "@table_Y\n"; print INFOTXT "Cb\n"; print INFOTXT "@table_Cb\n"; print INFOTXT "Cr\n"; print INFOTXT "@table_Cr\n"; close INFOTXT; ############################################################################ #-----------------------------------------# # subrountine definition # #-----------------------------------------# sub process_H_format { my $data = join '', @rgb_data; # reorder the image data my $offset = $header{ 'biWidth' } * 3 * 2; my @lines = reverse ($data =~ /([\w\d]{$offset})/ig ); #print "@lines\n"; my $reorder_data = join '', @lines; # while ( $reorder_data =~ /([\w\d]{2})([\w\d]{2})([\w\d]{2})/ig ) { push @table_R, $3; push @table_G, $2; push @table_B, $1; #print "R=>$3 : G=>$2 : B=>$1\n"; } } sub process_C_format { # reorder the image data my @reorder_data; for( my $y=0; $y < $header{ 'biHeight' }; $y++ ) { my @lines; my $x_count = $header{ 'biWidth' } * 3; while( $x_count-- ) { push @lines, pop @rgb_data; } #print "@lines\n"; unshift @reorder_data, @lines; } #print "@reorder_data\n"; my $size = $header{ 'biWidth' } * $header{ 'biHeight' }; while ( $size-- ) { #print $size, "\n"; push @table_B, pop @reorder_data; push @table_G, pop @reorder_data; push @table_R, pop @reorder_data; } } # prepare the YCbCr data sub prepare_Y_LUT { for ( 0..255 ) { $YR_LUT[$_] = int(65536*0.299+0.5) * $_; $YG_LUT[$_] = int(65536*0.587+0.5) * $_; $YB_LUT[$_] = int(65536*0.114+0.5) * $_; } } sub prepare_Cb_LUT { for ( 0..255 ) { $CbR_LUT[$_] = int(65536*-0.16874+0.5) * $_; $CbG_LUT[$_] = int(65536*-0.33126+0.5) * $_; $CbB_LUT[$_] = 32768 * $_; } } sub prepare_Cr_LUT { for ( 0..255 ) { $CrR_LUT[$_] = 32768 * $_; $CrG_LUT[$_] = int(65536*-0.41869+0.5) * $_; $CrB_LUT[$_] = int(65536*-0.08131+0.5) * $_; } } |
相关阅读 更多 +