Menu Close

Verilog文件操作练习(1)BMP图像色彩替换

之前学习了许多和Verilog文件操作相关的函数,比如$fdisplay,$fwrite,$fseek,$ftell,$feof,$fgetc,$fgets,$fread等,本节课将结合BMP图像的特点,练习使用其中几个函数操作BMP文件。

1. BMP格式

在开始使用Verilog文件操作函数之前,先简单介绍一下BMP文件的格式。BMP(bitmap,位图),也通常被称为DIB(device independent bitmap,与设备无关的位图)。BMP文件是独立于显示器的位图数字图像文件格式(关于图像色彩的内容,比如RGB24,详见文章 图像色彩空间转换 。)。BMP是Windows系统常用的图像存储格式,通常以扩展名 “ *.bmp ” 的方式出现。因为BMP文件格式与设备无关,所以BMP文件经常在设备之间传送。

 

BMP文件格式如下表所示:

表1:

结构名称 可选/必选 尺寸 目的 描述
位图文件头(Bitmap file header) 必选 14 个字节 存储有关位图图像文件的一般信息 文件加载到内存后不需要
DIB 文件头 必选 固定大小
(存在 7 个不同的版本)
存储有关位图图像的详细信息并定义像素格式 紧跟在位图文件头之后,最通用的DIB文件头为BITMAPINFOHEADER,长度为40个字节
额外的位掩码(Extra bit masks) 可选 3 或 4个 DWORD
(12 或 16 字节)
定义像素格式 仅在 DIB 标头是BITMAPINFOHEADER且 Compression Method 成员设置为 BI_BITFIELDS 或 BI_ALPHABITFIELDS 的情况下出现
颜色表(Color table) 可选 可变 定义位图图像数据(像素数组)使用的颜色 强制颜色深度( color depth)≤ 8 位
间隙1(Gap 1) 可选 可变 结构对齐 位图文件头中的文件偏移到像素阵列的工件
像素阵列(Pixel array) 必选 可变 定义像素的实际值 像素格式由 DIB 标头或额外位掩码定义。Pixel 数组中的每一行都被填充为 4 字节大小的倍数
间隙2(Gap 2) 可选 可变 结构对齐 DIB 头中的 ICC 配置文件数据偏移字段的工件
ICC 颜色配置文件(ICC color profile) 可选 可变 定义颜色管理的颜色配置文件 还可以包含指向包含颜色配置文件的外部文件的路径。当作为“非压缩 DIB”加载到内存中时,它位于颜色表和 Gap1 之间。

 

  • BMP文件头应该是应用程序首先读取的,来确保该文件确实为BMP文件并且没有被损坏,可以正常读出。BMP文件的总长度从BMP文件头的偏移量0x2开始,共两个字节。
  • DIB文件头告诉应用程序有关图像的详细信息,用来在屏幕上显示图像,一般常用的BITMAPINFOHEADER格式下,DIB文件头的长度为40个字节。
  • 颜色表是一个字节表,通常以RGB24格式指定,用来列出位图使用的颜色,以便在颜色显示能力有限的设备上进行优化,便于将来转换为不同的像素格式和颜色表。
  • 像素阵列是以32-bit为单位排列,逐个像素的描述图像(宽度不足4个字节的BMP图像需要加上padding)。图像像素描述的规律一般是从下到上,从左到右,逐行从图像的底部到顶部。

以一个8*8(没有padding)的BMP图像为例来说明BMP格式:

图像如下所示(为了清楚展现,做了拉伸放大处理):

图1 BMP图像

使用HEX 格式打开该BMP文件,如下所示:

图2 HEX格式打开BMP文件

分析该文件如下表所示:

表2:

偏移量 大小(字节) HEX值(16进制表示) 描述
位图文件头(Bitmap file header)
0h 2 42 4D ID
2h 4 F6 00 00 00 BMP文件的大小(54 字节的文件头 +  192字节的像素色彩),54 = 14(位图文件头) + 40(DIB文件头),192 = 8 * 8 * 3(8*8个像素,每个像素使用RGB24描述,3个字节)
6h 2 00 00 保留
8h 2 00 00 保留
Ah 4 36 00 00 00 可以找到像素阵列(位图数据)的偏移量
DIB 文件头
Eh 4 28 00 00 00 DIB 标头中的字节数(从这一点开始),为40个字节
12h 4 08 00 00 00 位图的宽度(以像素为单位)
16h 4 08 00 00 00 位图的高度(以像素为单位)。像素顺序是从下到上的。
1Ah 2 01 00 使用的颜色平面数量
1Ch 2 18 00 每个像素使用的位数,这里因为是RGB24,所以是0x18
1Eh 4 00 00 00 00 不适用像素阵列压缩
22h 4 C0 00 00 00 位图数据(像素阵列)的大小,为192个字节
26h 4 C4 E0 00 00 图像的打印分辨率,每米3780(0xEC4) 个像素(水平/垂直)
2Ah 4 C4 E0 00 00
2Eh 4 00 00 00 00 调色盘中的颜色数量
32h 4 00 00 00 00 0表示所有的颜色都是重要的
像素阵列(Pixel array)
36h 3 00 00 FF 红色
39h 3 FF 00 00 蓝色
3Ch 3 13 13 FF 浅一点的蓝色
3Fh 3 FF 68 68 粉红色
… … … …
F3h 3 00 FF 00 绿色

2. BMP实验

实验目的:使用Verilog文件操作函数将BMP文件(假设DIB文件头格式为BITMAPINFOHEADER,图像宽度是4的倍数,不考虑padding,可参见表2)中所有的蓝色成分和红色成分交换,例如,某个像素的RGB值为0xff0000,处理后输出该像素为0x0000ff。如下图所示为RGB24的格式中蓝色成分和红色成分的位置:

 

图3 RGB24格式

 

  • 实验代码(仿真Testbench)如下:
`timescale 1ns / 1ps

module bmp_test(
    );
    
//original bmp file
localparam FILE_TXT = "D:/Files_import/2022/0207/test3.bmp";
  
//new bmp file with color switched
localparam FILE_NEW = "D:/Files_import/2022/0207/pixel_new.bmp";

    
integer fpre,fnew; // two bmp files

integer i;//debuging purpose only

//parameter to adjust the endian, color and etc.
reg [7:0] fbyte1, fbyte2, fbyte3, fbyte4;
reg [7:0] fbyte [3:0];

integer num;//param used for $fread

reg [31:0] image_size;//param for counting image size
reg [31:0] image_width;//param for counting image width
reg [31:0] image_height;//param for counting image height

initial
begin
i = 0;

fpre = $fopen(FILE_TXT, "rb"); //binary read
fnew = $fopen(FILE_NEW,"wb");  //binary write

if(fpre == 0)
    begin
        $display("$open file failed") ;
        $stop;
    end
    $display("\n ============= file opened... ============= ") ;

 
//================= BMP HEADER + DIB HEADER ================
//byte 0-1
    num = $fread(fbyte,fpre,0,2);
    i = i + 2;
    #5;
    
    $fwrite(fnew,"%c",fbyte[0]);
    
    $fwrite(fnew,"%c",fbyte[1]);
//BYTE 0x2-0x5  image size
    num = $fread(fbyte,fpre,0,4);
    i = i + 4;
    
    $fwrite(fnew,"%c",fbyte[0]);
    $fwrite(fnew,"%c",fbyte[1]);
    $fwrite(fnew,"%c",fbyte[2]);
    $fwrite(fnew,"%c",fbyte[3]);
    
    image_size = {fbyte[3],fbyte[2],fbyte[1],fbyte[0]};
    #10;
    
    $display("The total image size is %d\n",image_size) ;
    #10;

//Byte 0x6-0x11
    while(i < 32'h12)
    begin
        num = $fread(fbyte1,fpre,0,1);
        i = i + 1;
        $fwrite(fnew,"%c",fbyte1);
    end
    
//BYTE 0x12-0x15  image width
    num = $fread(fbyte,fpre,0,4);
    i = i + 4;
    
    $fwrite(fnew,"%c",fbyte[0]);
    $fwrite(fnew,"%c",fbyte[1]);
    $fwrite(fnew,"%c",fbyte[2]);
    $fwrite(fnew,"%c",fbyte[3]);
    
    image_width = {fbyte[3],fbyte[2],fbyte[1],fbyte[0]};
    
    $display("The total image width is %d\n",image_width) ;
    #10;


//BYTE 0x16-0x19  image height
    num = $fread(fbyte,fpre,0,4);
    i = i + 4;
    
    $fwrite(fnew,"%c",fbyte[0]);
    $fwrite(fnew,"%c",fbyte[1]);
    $fwrite(fnew,"%c",fbyte[2]);
    $fwrite(fnew,"%c",fbyte[3]);
    
    image_height = {fbyte[3],fbyte[2],fbyte[1],fbyte[0]};
    #10;
    
    $display("The total image width is %d\n",image_height) ;
    #10;


//byte 0x20-0x36
    while(i < 32'h36)
    begin
        num = $fread(fbyte1,fpre,0,1);
        i = i + 1;
        $fwrite(fnew,"%c",fbyte1);
    end

//====================== PIXCEL ARRAY ======================
////BYTE 0x36  image height

    while($feof(fpre) != 1)
    begin
        num = $fscanf(fpre,"%c",fbyte1);
        num = $fscanf(fpre,"%c",fbyte2);
        num = $fscanf(fpre,"%c",fbyte3);
        
        $fwrite(fnew,"%c",fbyte3);//		red
        $fwrite(fnew,"%c",fbyte2);//		green
        $fwrite(fnew,"%c",fbyte1);//		blue
    
    end

    
    $fclose(fpre);
    $fclose(fnew);
    $display("\n ============= file closed... ============= ") ;
    #100;
    $stop;

end
   

endmodule

 

  • 图像变换演示:
    • 例1:8*8 像素的BMP图像

[content_control]

test1.zip

[/content_control]

原图:

图4 8*8 BMP图像

交换红,蓝元素后的图像:

图5 处理后的8*8 BMP图像

    • 例2:512*286风景图

[content_control]

test2.zip

[/content_control]

原图:

图6 512*286风景图

交换红,蓝元素后的图像:

图7 处理后的512*286风景图

    • 例3:316*313雷娜图

[content_control]

test3.zip

[/content_control]

原图:

 

图8 316*313雷娜图

交换红,蓝元素后的图像:

 

图9 处理后的316*313雷娜图

3. 总结

本文通过一个简单的例子,使用Verilog文件操作对BMP文件中像素的红,蓝元素进行了替换,学习了BMP文件格式和简单的图像处理。在此基础上,感兴趣的同学可以进行扩展学习,例如对于图像宽度不是4字节对齐,需要考虑padding的情况如何进行处理;例如对于不是RGB24而是RGBA32(使用4个字节而不是3个字节来表示每个像素的色彩模型)的BMP文件如何进行相应的变换等。

Posted in FPGA, FPGA 教材教案, Verilog, Verilog, Vivado, Vivado, 开发工具, 开发工具, 开发语言, 教材与教案, 文章, 编程语言

发表回复

相关链接