搭建图像处理仿真测试工程,实现读写bmp文件的功能
一、SystemVerilog/Verilog 读写文件函数
1. 打开文件函数
示例:
integer fd;
fd = $fopen("xxx.bmp","rb");
2. 关闭文件函数
3. 读写文件函数
(1) writemem[b|h]/readmem[b|h]
示例:
reg [31:0] mem [63:0];
initial begin
$readmemb("xxx./data.hex" , mem); //读文件数据
$dispaly("Read memory1: %h" , mem[0]);
$writememb("xxx./data_bak.hex" , mem);//写文件数据
end
(2) $fscanf 和 $fwrite
示例:读写1920个数据位宽为8bit的数据文件
parameter LEN = 1920;
integer i;
reg [7:0] data [LEN - 1:0];
integer fd;
initial begin
// 读文件
fd = $fopen("./in.txt","rb");
for( i=0; i<LEN; i=i+1)begin
$fscanf(fd ,"%c",data[i]);
$display("Read data is: %c" , data[i]);
end
$fclose(fd);
// 写文件
fd = $fopen("./out.txt","wb");
for( i=0; i<LEN; i=i+1)begin
$fwrite(fd ,"%c",data[i]);
end
$fclose(fd);
end
4. 文件定位函数
(1) 获取文件位置函数 $ftell
(2) 重定位函数 $fseek
二、BMP文件说明
以1 x 1 的像素为例:
三、图像处理仿真测试工程
1. 图像处理仿真测试工程框架
2. 工程设计
(1)定义图片文件位置、不同分辨率的图片大小、以及图像的横/纵向的消隐区大小
//`define pix_1920_1080
`define pix_1280_720
`ifdef pix_1920_1080
`define INPUT_FILE "../../../../test_img/in/1920_1080.bmp" //input image
`define IMG_WIDTH 1920 //Image width
`define IMG_HEIGHT 1080 //Image height
`define H_BLANK 720 //横向消影区,仿真可自由设定
`define V_BLANK 45 //纵向消影区,仿真可自由设定
`endif
`ifdef pix_1280_720
`define INPUT_FILE "../../../../test_img/in/1280_720.bmp" //input image
`define IMG_WIDTH 1280 //Image width
`define IMG_HEIGHT 720 //Image height
`define H_BLANK 480 //横向消影区,仿真可自由设定
`define V_BLANK 30 //纵向消影区,仿真可自由设定
`endif
`define OUTPUT_FILE "../../../../test_img/out/result.bmp" //result image frame1
(2)读取BMP文件的数据
先初始化图片RGB数据,再读取BMP图片文件,对BMP的信息头以及BGR数据进行更新
`define LEN_HEADER 54// 0x33 bmp文件头 + 信息头
`define IMG_ALL (`IMG_HEIGHT*`IMG_WIDTH*3) //图片像素总数据 RGB
`define IMG_SIZE (`IMG_HEIGHT*`IMG_WIDTH) //图片大小
`define IMG_BMP_SIZE (`IMG_ALL + `LEN_HEADER) //BMP图片总大小
`define NULL 0
`define DATA_WIDTH 8
integer ret, i, j, idx;
integer fdI;
reg [`DATA_WIDTH-1:0] bmp_head_r[0:`LEN_HEADER-1];//Image 1 Header
reg [`DATA_WIDTH-1:0] imgR_r[0:`IMG_SIZE-1]; //Image-- R Channel
reg [`DATA_WIDTH-1:0] imgG_r[0:`IMG_SIZE-1]; //Image-- G Channel
reg [`DATA_WIDTH-1:0] imgB_r[0:`IMG_SIZE-1]; //Image-- B Channel
wire [31:0] bmp_size, bmp_width, bmp_height;
reg init_r;
///////////////////////////////////读取BMP文件
assign bmp_size = {bmp_head_r[5] , bmp_head_r[4] , bmp_head_r[3] , bmp_head_r[2] };
assign bmp_width = {bmp_head_r[21], bmp_head_r[20], bmp_head_r[19], bmp_head_r[18]};
assign bmp_height = {bmp_head_r[25], bmp_head_r[24], bmp_head_r[23], bmp_head_r[22]};
initial begin
init_r = 1'b0;
for (i = 0; i < `IMG_SIZE; i=i+1)begin
imgB_r[i] = 0;
imgG_r[i] = 0;
imgR_r[i] = 0;
end
fdI = $fopen(`INPUT_FILE,"rb");
if (fdI == `NULL) begin
$display("> OPEN FAIL: The file not exist !!!");
end else begin
$display("> OPEN file SUCCESS !");
//读取bmp文件头
ret = $fread(bmp_head_r, fdI, 0, `LEN_HEADER);
//读取图像RGB分量值
//BMP倒序存储数据时,从下到上,从左到右
for(i=`IMG_HEIGHT - 1;i >= 0;i=i-1)
for(j=0;j <`IMG_WIDTH;j=j+1) begin
idx = i*`IMG_WIDTH + j;
imgB_r[idx] = $fgetc(fdI);//b
imgG_r[idx] = $fgetc(fdI);//g
imgR_r[idx] = $fgetc(fdI);//r
end
$display("> Read b,g,r Successful !");
end
(3)写BMP文件数据
首先将读出来的数据进过图像处理算法的模块,产生输出数据;这边图像处理的算法模块就是简单的原图数据,本文只是为了验证框架的有效性。
然后先写入文件的头信息,再将模块的数据数据按照倒序存储BGR数据,需要移动文件内的偏移量
integer fdO;
integer file_end_offset, zero_len;
reg [31:0] h_cnt_r, v_cnt_r;
reg [31:0] data_cnt_r;
reg [31:0] addr_r;
wire valid_i;
wire [`DATA_WIDTH*3-1:0] img_data_i;
wire valid_o;
wire [`DATA_WIDTH*3-1:0] img_data_o;
reg [31:0] out_data_cnt_r;
reg valid_o_r;
wire [`DATA_WIDTH-1:0] R_o_w, G_o_w, B_o_w;
///////////////////////////////////图像算法模块
image_process u_image_process(
.clk ( clk ),
.reset ( reset ),
.img_width ( `IMG_WIDTH ),
.img_height ( `IMG_HEIGHT ),
.valid_i ( valid_i ),
.img_data_i ( img_data_i ),
.valid_o ( valid_o ),
.img_data_o ( img_data_o )
);
always @(posedge clk or posedge reset) begin
if (reset) begin
{h_cnt_r, v_cnt_r} <= 'b0;
addr_r <= 'b0;
data_cnt_r <= 'b0;
end else begin
h_cnt_r <= (h_cnt_r == `IMG_WIDTH + `H_BLANK - 1) ? 'b0 : h_cnt_r + 1'b1;
v_cnt_r <= (h_cnt_r == `IMG_WIDTH + `H_BLANK - 1) ? ((v_cnt_r == `IMG_HEIGHT + `V_BLANK - 1) ? 'b0 : v_cnt_r + 1'b1) : v_cnt_r;
addr_r <= (addr_r == `IMG_SIZE - 1) ? 'b0 : valid_i ? (addr_r + 'd1) : addr_r;
data_cnt_r <= valid_i ? ((data_cnt_r == `IMG_WIDTH - 1) ? 'b0 : data_cnt_r + 1) : data_cnt_r;
end
end
assign valid_i = (h_cnt_r < `IMG_WIDTH)&&(v_cnt_r < `IMG_HEIGHT) ? 1'b1 : 1'b0;
assign img_data_i = {imgR_r[addr_r], imgG_r[addr_r], imgB_r[addr_r]};
fdO = $fopen(`OUTPUT_FILE,"wb");
//写入文件头
for(i=0;i < `LEN_HEADER;i=i+1) begin
$fwrite(fdO, "%c", bmp_head_r[i]);
end
//移动到图片数据最后一行起始位置
file_end_offset = `IMG_ALL + `LEN_HEADER - `IMG_WIDTH*3;
$fseek(fdO, file_end_offset, `SEEK_SET);
init_r = 1'b1;
end
///////////////////////////////////写入BMP文件
assign {R_o_w, G_o_w, B_o_w} = img_data_o;
always @(posedge clk or posedge reset) begin
if (reset) begin
out_data_cnt_r <= 'b0;
valid_o_r <= 'b0;
end else begin
valid_o_r <= valid_o;
if(valid_o) begin
$fwrite(fdO, "%c", B_o_w);
$fwrite(fdO, "%c", G_o_w);
$fwrite(fdO, "%c", R_o_w);
out_data_cnt_r <= out_data_cnt_r + 1'b1;
end else if(valid_o_r) begin//行结束
file_end_offset = file_end_offset - `IMG_WIDTH*3;
$fseek(fdO, file_end_offset, `SEEK_SET);
end
end
end
(4)仿真流程
///////////////////////////////////时钟和初始化
//时钟
always
begin
#(TIME/2) clk = ~clk;
end
//仿真控制
initial begin
reset = 1'b1;
zero_len = 'b0;
#(TIME*10);
reset = 1'b0;
wait(init_r);
$display("> Initial Done");
wait(out_data_cnt_r == (`IMG_SIZE-1));//检测一幅图像完成
#(TIME*2);
//文件末尾补0
$fseek(fdO, 0, `SEEK_END);
zero_len = $ftell(fdO);
zero_len = 4- zero_len[1:0];
for(i=0;i < zero_len;i=i+1)
$fwrite(fdO, "%c", 0);
$fclose(fdO);
$display("> Simulate Done");
$stop;
end
3.仿真结果
成功建立仿真流程,以后的图像处理算法模块PL端编写完之后,先经过该仿真工程对算法进行验证
本文算法是原样输出,结果正确。