版权所有:转载请注明 https://blog.csdn.net/jayash/article/details/79947314
基于FPGA的图像处理中,rtl代码的仿真验证一直是重中之重, 之前也在我们的书中《基于FPGA的数字图像处理原理及应用》(电子工业出版社)中提出了基于VC和verilog的仿真验证平台。该验证平台仅能提供简单的图像数据的交互,并且需要VC平台的交互。VC可提供较好的UI功能,但是对于一些中间结果的验证还是比较麻烦。最近刚好学习了system verilog,其面对对象的设计特性很类似于C++,可完整实现更加方便和功能更加全面的testbench平台。
今天主要介绍bmp文件的读取及解析。bmp文件的格式详解可参考以下链接:
https://www.cnblogs.com/l2rf/p/5643352.html
bmp文件解析主要是bmp文件头的解析以及图像数据内容的重定位。bmp文件头主要类型定义如下:
typedef struct{ u32 biSize; u32 biWidth; u32 biHeight; u16 biPlanes; u16 biBitCount; u32 biCompression; u32 biSizeImage; u32 biXPelsPerMeter; u32 biYPelsPerMeter; u32 biClrUsed; u32 biClrImportant; } BITMAPINFOHEADER; typedef struct { u8 rgbBlue; u8 rgbGreen; u8 rgbRed; u8 rgbReserved; } RGBQUAD; typedef struct { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO; typedef struct { u16 bfType; u32 bfSize; u16 bfReserved1; u16 bfReserved2; u32 bfOffBits; } BITMAPFILEHEADER;
bmp文件主要有两个header,包括一个14字节的header BITMAPFILEHEADER,包含了bmp文件头信息,以及 一个40字节的header BITMAPINFOHEADER,包含了bmp文件的图像相关信息。
解析的主要要点有:
1 内存存放问题:分辨率不为4的倍数的每一行之后会补零,保证每一行的占用内存是4的倍数;
2 数据组织问题:bmp的组织原则是首先存放最后一行,接着倒数第二行,以此类推,解析时需还原;
解析代码如下:(代码为system verilog)
class spk_bmp; function void trans_16(reg [15:0] _reg,ref u16 dat); dat = (_reg[7:0]<<8) + _reg[15:8]; endfunction function void trans_32(reg [31:0] _reg,ref u32 dat); dat = (_reg[7:0]<<24)+ (_reg[15:8]<<16)+ (_reg[23:16]<<8) + _reg[31:24]; endfunction function int LoadVectorFromBMPFile(string name,ref u8 data[], ref u16 height,ref u16 width,ref u16 flag); u32 skip,position; u32 dwSize; u32 dReadWidth; reg [8*14-1:0] header1; reg [8*40-1:0] header2; reg [8-1:0] data_tmp; u32 off = 8*14-1; BITMAPFILEHEADER bfh; BITMAPINFO bmi; //判断文件是否存在 int fp = $fopen(name, "r"); if(!fp) begin $display("Error: can not open file: %s!",name); $stop; return -1; end //读header1 $fread(header1,fp,0,14); off = 14*8 - 1; trans_16(header1[off -: 2*8],bfh.bfType); off -= 2*8; trans_32(header1[off -: 4*8],bfh.bfSize); off -= 4*8; trans_16(header1[off -: 2*8],bfh.bfReserved1); off -= 2*8; trans_16(header1[off -: 2*8],bfh.bfReserved2); off -= 2*8; trans_32(header1[off -: 4*8],bfh.bfOffBits); off -= 4*8; if(bfh.bfType != 16'h4d42) begin $display("Error: '%s' is not a bmp file!",name); $stop; return -1; end //读header2 $fread(header2,fp,14,40); off = 40*8 - 1; trans_32(header2[off -: 4*8],bmi.bmiHeader.biSize); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biWidth); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biHeight); off -= 4*8; trans_16(header2[off -: 2*8],bmi.bmiHeader.biPlanes); off -= 2*8; trans_16(header2[off -: 2*8],bmi.bmiHeader.biBitCount); off -= 2*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biCompression); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biSizeImage); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biXPelsPerMeter); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biYPelsPerMeter); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biClrUsed); off -= 4*8; trans_32(header2[off -: 4*8],bmi.bmiHeader.biClrImportant); off -= 4*8; height = bmi.bmiHeader.biHeight; width = bmi.bmiHeader.biWidth; flag = bmi.bmiHeader.biBitCount; if(flag == 24) width *= 3; //真彩图 dwSize = height * width; dReadWidth = (width+3)/4*4; data = new[dwSize]; skip = dReadWidth - width; position = dwSize - width; off = bfh.bfOffBits; //第一行图像数据偏移 $fseek(fp,off,0); for(int i = 0;i < height;i++) begin for(int j = 0;j < width;j++) begin $fread(data_tmp,fp,off,1); data[position] = data_tmp; off++; $fseek(fp,off,0); position++; end position -= 2*width; off += skip; end $fclose(fp); if(flag==24) width /= 3; endfunction endclass : spk_bmp