Moedlsim仿真图片—输入图片,仿真后输出图片

前言

在平时利用Modelsim仿真时,一般是利用它查看某些信号的波形和取值是否正常,但是在利用FPGA进行图像处理的时候,通常我们想直观的看到处理后的图片是否达到了预期效果,因此产生了一个思考,能否利用Modelsim对图片进行仿真。在网上查找资料后,发现大磊出过一个视频教学,在经过学习后,成功实现,故写一篇帖子和大家分享学习。

原理

解析BMP图片+模拟摄像头时序
说通俗一点,就是在tb文件中对BMP格式的图片进行解析,用数组统计其长度宽度像素值等参数;然后写一个摄像头时序代码,利用模拟摄像头将这个模拟的图片输入进行处理,最后将输出的数组转换为BMP格式的图片输出

代码

BMP图片解析为数组

integer iBmpFileId;                 

integer oTxtFileId;                 
        
integer iIndex = 0;                 

integer iCode;      
        
integer iBmpWidth;                  //输入BMP 宽度
integer iBmpHight;                  //输入BMP 高度
integer iBmpSize;                   //输入BMP 字节数
integer iDataStartIndex;            //输入BMP 像素数据偏移量

localparam BMP_SIZE   = 54 + IMG_HDISP * IMG_VDISP * 3 - 1;     //输出BMP 字节数
    
reg [ 7:0] rBmpData [0:BMP_SIZE];  

integer i,j;


//---------------------------------------------
initial begin
	iBmpFileId	= 	$fopen(PIC_PATH,"rb");

	iCode = $fread(rBmpData,iBmpFileId);

	//根据BMP图片文件头的格式,分别计算出图片的 宽度 /高度 /像素数据偏移量 /图片字节数
	iBmpWidth       = {rBmpData[21],rBmpData[20],rBmpData[19],rBmpData[18]};
	iBmpHight       = {rBmpData[25],rBmpData[24],rBmpData[23],rBmpData[22]};
	iBmpSize        = {rBmpData[ 5],rBmpData[ 4],rBmpData[ 3],rBmpData[ 2]};
	iDataStartIndex = {rBmpData[13],rBmpData[12],rBmpData[11],rBmpData[10]};
	
	//关闭输入BMP图片
	$fclose(iBmpFileId);
end

模拟摄像头时序(OV7725/5640)

//产生摄像头时序 

wire		cmos_vsync ;
reg			cmos_href;
wire        cmos_clken;
reg	[23:0]	cmos_data;	
		 
reg         cmos_clken_r;

reg [31:0]  cmos_index;

localparam H_SYNC = 11'd10;		
localparam H_BACK = 11'd10;		
localparam H_DISP = IMG_HDISP;	
localparam H_FRONT = 11'd10;		
localparam H_TOTAL = H_SYNC + H_BACK + H_DISP + H_FRONT;	

localparam V_SYNC = 11'd10;		
localparam V_BACK = 11'd10;		
localparam V_DISP = IMG_VDISP;	
localparam V_FRONT = 11'd10;		
localparam V_TOTAL = V_SYNC + V_BACK + V_DISP + V_FRONT;

//---------------------------------------------
//模拟 OV7725/OV5640 驱动模块输出的时钟使能
always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		cmos_clken_r <= 0;
	else
        cmos_clken_r <= ~cmos_clken_r;
end

//---------------------------------------------
//水平计数器
reg	[10:0]	hcnt;
always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		hcnt <= 11'd0;
	else if(cmos_clken_r) 
		hcnt <= (hcnt < H_TOTAL - 1'b1) ? hcnt + 1'b1 : 11'd0;
end

//---------------------------------------------
//竖直计数器
reg	[10:0]	vcnt;
always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		vcnt <= 11'd0;		
	else if(cmos_clken_r) begin
		if(hcnt == H_TOTAL - 1'b1)
			vcnt <= (vcnt < V_TOTAL - 1'b1) ? vcnt + 1'b1 : 11'd0;
		else
			vcnt <= vcnt;
    end
end

//---------------------------------------------
//场同步
reg	cmos_vsync_r;
always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		cmos_vsync_r <= 1'b0;		//H: Vaild, L: inVaild
	else begin
		if(vcnt <= V_SYNC - 1'b1)
			cmos_vsync_r <= 1'b0; 	//H: Vaild, L: inVaild
		else
			cmos_vsync_r <= 1'b1; 	//H: Vaild, L: inVaild
    end
end
assign	cmos_vsync	= cmos_vsync_r;

//---------------------------------------------
//行有效
wire	frame_valid_ahead =  ( vcnt >= V_SYNC + V_BACK  && vcnt < V_SYNC + V_BACK + V_DISP
                            && hcnt >= H_SYNC + H_BACK  && hcnt < H_SYNC + H_BACK + H_DISP ) 
						? 1'b1 : 1'b0;
      
reg			cmos_href_r;      
always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		cmos_href_r <= 0;
	else begin
		if(frame_valid_ahead)
			cmos_href_r <= 1;
		else
			cmos_href_r <= 0;
    end
end

always@(posedge clk or negedge rst_n) begin
	if(!rst_n)
		cmos_href <= 0;
	else
        cmos_href <= cmos_href_r;
end

assign cmos_clken = cmos_href & cmos_clken_r;

//-------------------------------------
//从数组中以视频格式输出像素数据
wire [10:0] x_pos;
wire [10:0] y_pos;

assign x_pos = frame_valid_ahead ? (hcnt - (H_SYNC + H_BACK )) : 0;
assign y_pos = frame_valid_ahead ? (vcnt - (V_SYNC + V_BACK )) : 0;

always@(posedge clk or negedge rst_n)begin
   if(!rst_n) begin
       cmos_index   <=  0;
       cmos_data    <=  24'd0;
   end
   else begin
       cmos_index   <=  y_pos * IMG_HDISP * 3  + x_pos * 3 + 54;         //  3*(y*640 + x) + 54
       cmos_data    <=  {rBmpData[cmos_index], rBmpData[cmos_index+1] , rBmpData[cmos_index+2]};
   end
end
 
reg [10:0] x_pos_d	[0 : 10];
reg [10:0] y_pos_d	[0 : 10];

always@(posedge clk or negedge rst_n)begin
	if(!rst_n)begin
		for(i = 0; i < 11; i = i + 1)begin
			x_pos_d[i]	<=	0;
			y_pos_d[i]	<=	0;
		end	
	end
	else begin
		x_pos_d[0]	<=	x_pos;
		y_pos_d[0]	<=	y_pos;
		for(i = 1; i < 11; i = i + 1)begin
			x_pos_d[i]	<=	x_pos_d[i-1];
			y_pos_d[i]	<=	y_pos_d[i-1];
		end	
	end
end

assign		CMOS_VSYNC		=	cmos_vsync;
assign		CMOS_HREF		=	cmos_href;
assign		CMOS_CLKEN		=	cmos_clken;
assign		CMOS_DATA		=	cmos_data;
assign		X_POS			=	x_pos;
assign		Y_POS			=	y_pos;

图片恢复及存储处理

integer iCode;
integer iBmpFileId;             
integer iBmpWidth;              
integer iBmpHight;              
integer iBmpSize;               
integer iDataStartIndex;        
integer iIndex = 0;

localparam BMP_SIZE   = 54 + IMG_HDISP * IMG_VDISP * 3 - 1;  
reg [ 7:0] BmpHead          [0:53];
reg [ 7:0] Vip_BmpData      [0:BMP_SIZE]; 
reg [ 7:0] vip_pixel_data   [0:BMP_SIZE-54];
reg [31:0] rBmpWord;

reg             video_vsync_d1 = 0;
reg     [11:0]  frame_cnt = 0;
reg     [31:0]  PIC_cnt = 0;

wire    [7:0]   PIC_img_R;
wire    [7:0]   PIC_img_G;
wire    [7:0]   PIC_img_B;
assign          PIC_img_R = video_data[16+:8];
assign          PIC_img_G = video_data[ 8+:8];
assign          PIC_img_B = video_data[ 0+:8];


always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        video_vsync_d1   <=  0;
    end
    else begin
        video_vsync_d1  <=  video_vsync;
    end
end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        frame_cnt   <=  0;
    end
    else if(video_vsync_d1 & !video_vsync)begin
        frame_cnt   <=  frame_cnt + 1;
    end
end



always@(posedge clk or negedge rst_n)begin
   if(!rst_n) begin
        PIC_cnt <=  32'd0;
   end
   else if(video_de) begin
        if(frame_cnt == START_FRAME - 1) begin
            PIC_cnt                     <=  PIC_cnt + 3;
            vip_pixel_data[PIC_cnt+0]   <=  PIC_img_R;
            vip_pixel_data[PIC_cnt+1]   <=  PIC_img_G;
            vip_pixel_data[PIC_cnt+2]   <=  PIC_img_B;
        end
   end
end


//---------------------------------------------	
//initial the BMP file header
//The detail BMP file header is inpart from https://blog.csdn.net/Meteor_s/article/details/82414155 and inpart from my test. 
initial begin
    for(iIndex = 0; iIndex < 54; iIndex = iIndex + 1) begin
        BmpHead[iIndex] = 0;
    end
    #2
    {BmpHead[1],BmpHead[0]} = {8'h4D,8'h42};
    {BmpHead[5],BmpHead[4],BmpHead[3],BmpHead[2]} = BMP_SIZE + 1;//File Size (Bytes)
    BmpHead[10] = 8'd54;//Bitmap Data Offset
    BmpHead[14] = 8'h28;//Bitmap Header Size
    {BmpHead[21],BmpHead[20],BmpHead[19],BmpHead[18]} = IMG_HDISP;//Width
    {BmpHead[25],BmpHead[24],BmpHead[23],BmpHead[22]} = IMG_VDISP;//Height
    BmpHead[26] = 8'd1;//Number of Color Planes
    BmpHead[28] = 8'd24;//Bits per Pixel
end



//---------------------------------------------	
//write the data to the output bmp file
initial begin    
//---------------------------------------------	
//waiting for the start frame
    wait(frame_cnt == START_FRAME);
    
    iBmpFileId = $fopen(PIC_PATH,"wb+");
    for (iIndex = 0; iIndex < BMP_SIZE + 1; iIndex = iIndex + 1) begin
        if(iIndex < 54) begin
            Vip_BmpData[iIndex] = BmpHead[iIndex];
        end
        else begin
            Vip_BmpData[iIndex] = vip_pixel_data[iIndex-54];
        end
    end  
    for (iIndex = 0; iIndex < BMP_SIZE + 1; iIndex = iIndex + 1) begin
        $fwrite(iBmpFileId,"%c",Vip_BmpData[iIndex]);
    end
    $fclose(iBmpFileId);
    $display("The picture is saved in %s",PIC_PATH);
    $stop;
end

处理调用

`ifdef Modelsim_Sim    
localparam	PIC_INPUT_PATH  	= 	"C:\\Users\\saoge\\Desktop\\FPGA\\FPGA3\\defog\\PIC\\ceshi1.bmp"			;
localparam	PIC_OUTPUT_PATH 	= 	"C:\\Users\\saoge\\Desktop\\FPGA\\FPGA3\\defog\\PIC\\outcom2.bmp"  		;
`endif
//--------------------------------------------------------------------------------
`ifdef Vivado_Sim
localparam	PIC_INPUT_PATH  	= 	"../../../../../PIC/duck.bmp"			;
localparam	PIC_OUTPUT_PATH 	= 	"../../../../../PIC/outcom.bmp"  		;
`endif

localparam	PIC_WIDTH  			=	640							;
localparam	PIC_HEIGHT 			=	480 						;

reg         cmos_clk   = 0;
reg         cmos_rst_n = 0;

wire        cmos_vsync              ;
wire        cmos_href               ;
wire        cmos_clken              ;
wire [23:0] cmos_data               ;

wire        haze_removal_vsync      ;
wire        haze_removal_hsync      ;
wire        haze_removal_de         ;
wire [23:0] haze_removal_data       ;

parameter cmos0_period = 6;

always#(cmos0_period/2) cmos_clk = ~cmos_clk;
initial #(20*cmos0_period) cmos_rst_n = 1;

//--------------------------------------------------
//Camera Simulation
sim_cmos #(
		.PIC_PATH			(PIC_INPUT_PATH			)
	,	.IMG_HDISP 			(PIC_WIDTH 				)
	,	.IMG_VDISP 			(PIC_HEIGHT				)
)u_sim_cmos0(
        .clk            	(cmos_clk	    		)
    ,   .rst_n          	(cmos_rst_n     		)
	,   .CMOS_VSYNC     	(cmos_vsync     		)
	,   .CMOS_HREF      	(cmos_href      		)
	,   .CMOS_CLKEN     	(cmos_clken     		)
	,   .CMOS_DATA      	(cmos_data      		)
	,   .X_POS          	()
	,   .Y_POS          	()
);

//--------------------------------------------------
//Image Processing
haze_removal_top #(
	 	.Y_ENHANCE_ENABLE	(0						)
	,	.PIC_WIDTH			(PIC_WIDTH				)
)u_haze_removal_top(
		.clk               	(cmos_clk	            )
	,	.rst_n             	(cmos_rst_n             )
	//处理前数据
	,	.pre_frame_vsync   	(cmos_vsync             )
	,	.pre_frame_href    	(cmos_href              )
	,	.pre_frame_clken   	(cmos_clken             )
	,	.pre_img           	(cmos_data              )
	//处理后的数据
	,	.post_frame_vsync  	(haze_removal_vsync		)
	,	.post_frame_href   	(haze_removal_hsync		)
	,	.post_frame_clken  	(haze_removal_de   		)
	,	.post_img          	(haze_removal_data      )
);

//--------------------------------------------------
//Video saving 
video_to_pic #(
		.PIC_PATH       	(PIC_OUTPUT_PATH		)
	,	.START_FRAME    	(2                      )
	,	.IMG_HDISP      	(PIC_WIDTH 				)
	,	.IMG_VDISP      	(PIC_HEIGHT				)
)u_video_to_pic0(
	 	.clk            	(cmos_clk	            )
	,	.rst_n          	(cmos_rst_n             )
	,	.video_vsync    	(haze_removal_vsync		)
	,	.video_hsync    	(haze_removal_hsync		)
	,	.video_de       	(haze_removal_de   		)
	,	.video_data     	(haze_removal_data      )
)

在处理调用时,其实就是测试文件的TOP文件,除了进行解析模拟输出外,还需要对图片进行处理,这里的代码是对图片进行了去雾操作(关于去雾算法可以去我前面的博客看)

结果

在这里插入图片描述
输入一张图片,输出仿真后的图片,该代码可用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CrazyCmt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值