【FPGA目标跟踪】基于FPGA的帧差法和SAD匹配算法的目标跟踪实现

1.软件版本

quartusii12.1

2.系统FPGA实现

FPGA整体的算法流程如下图所示:

 FPGA的模块主要包括如下几大模块:

摄像机驱动程序,SDRAM控制程序,显示屏驱动程序,中值滤波程序,帧差法模块,SAD模板匹配模块,跟踪定位模块等等。

跟踪模块顶层程序

帧差法模块

SAD模板匹配模块,R通道

SAD模板匹配模块,G通道

SAD模板匹配模块,B通道

显示定位跟踪效果

中值滤波,R通道

中值滤波,G通道

中值滤波,B通道

跟踪模块

开发板LED控制模块

系统复位模块

摄像机视频图像捕获模块

摄像机采集数据转换为RGB像素的模块

开发板数码管模块

SDAM核

SDRAM控制器

I2C配置,主要用来配置相机的初始化状态。

1.帧差法模块

       这个部分,就是将当前一帧图像,延迟一帧,然后相减,就可以得到。这个也是传统的差分,但是这种方法误差极大。主要包括:

        目标跟踪中,基础的差分法,存在较大的缺陷,比如目标静止状态,那么就无法检测,如果场景中,运动的物体非常多,那么就会出现检测混乱的情况,如果摄像机存在较为剧烈的抖动,同样会导致检测失败。然后我们考虑到在实际中,各种跟踪目标具有一定的特殊性。因此,针对各种不同的应用场景,设置不同的模板,结合传统的帧差法,从而提升系统的性能。

        帧差,其FPGA成像如下所示:

2.SAD模板匹配模块,RGB通道

这个部分的代码如下所示:

       这个模块里面,我们选择的窗口大小为20*20,即在进行模板匹配的时候,选择20*20区域内的像素进行求和运算。

       这个模块的实现过程是延迟,相加实现。

相当于公式:

3.显示定位跟踪效果

将差分,和模板匹配得到后的图像用一个轮廓进行表示。具体可以看仿真录像。

4.中值滤波,RGB通道

这个将图像中3*3区域内的图像的像素值,取其中间值来实现,具体可以参考中值滤波的相关原理。

5.跟踪模块

这个是根据差分帧,进行二值判决,得到大概的运动区域。

6.开发板LED控制模块

开发板硬件的配置,不涉及具体的算法原理

7.系统复位模块

开发板硬件的配置,不涉及具体的算法原理

8.摄像机视频图像捕获模块

这个部分,提供了摄像机的具体型号的datasheet,可以参考下。

9.摄像机采集数据转换为RGB像素的模块

将摄像机采集到的数据,进行拆分,然后组成RGB形式输出。

10.开发板数码管模块

开发板硬件的配置,不涉及具体的算法原理

11.SDAM核

这个部分是调用PLL核,产生控制SDRAM的时钟信号。

12.SDRAM控制器

SDRAM控制器,开发板硬件的配置,不涉及具体的算法原理

13.I2C配置,主要用来配置相机的初始化状态

I2C总线配置。开发板硬件的配置,不涉及具体的算法原理

整个系统的资源占用情况如下所示:

整个系统的RTL结构如下所示:

3.核心程序

//reset signals 
Reset_Delay			u2	(	.iCLK(CLOCK2_50),
							.iRST(KEY[0]),
							.oRST_0(DLY_RST_0),
							.oRST_1(DLY_RST_1),
							.oRST_2(DLY_RST_2),
							.oRST_3(DLY_RST_3), //auto-start start point
							.oRST_4(DLY_RST_4)  //auto-start end point
						);
//cmos sensor capture
CCD_Capture			u3	(	.oDATA(mCCD_DATA),
							.oDVAL(mCCD_DVAL),
							.oX_Cont(X_Cont),
							.oY_Cont(Y_Cont),
							.oFrame_Cont(Frame_Cont),
							.iDATA(rCCD_DATA),
							.iFVAL(rCCD_FVAL),
							.iLVAL(rCCD_LVAL),
							.iSTART((!KEY[3])| auto_start),
							.iEND(!KEY[2]),
							.iCLK(~CAMERA_PIXCLK),
							.iRST(DLY_RST_2)
						);
//raw data to RGB conversion
wire	[11:0]	sCCD_R_;
wire	[11:0]	sCCD_G_;
wire	[11:0]	sCCD_B_;
RAW2RGB				u4	(	.iCLK(CAMERA_PIXCLK),
							.iRST_n(DLY_RST_1),
							.iData(mCCD_DATA),
							.iDval(mCCD_DVAL),
							.oRed(sCCD_R_),
							.oGreen(sCCD_G_),
							.oBlue(sCCD_B_),
							.oDval(sCCD_DVAL),
							.iMIRROR(SW[17]),
							.iX_Cont(X_Cont),
							.iY_Cont(Y_Cont)
						);
					
//图像的中值滤波
//图像的中值滤波				
//图像的中值滤波	
wire	[11:0]	sCCD_R0;
wire	[11:0]	sCCD_G0;
wire	[11:0]	sCCD_B0;		
filter media_tops_u1(
								.i_clk          (CAMERA_PIXCLK), 
								.i_rst          (~DLY_RST_1), 
								.i_din          (sCCD_R_), 
								.o_address      (), 
								.o_enable0      (),//NC 
								.o_median       (sCCD_R0), 
								.o_enable1      (),//NC 
								.o_frame_flag   (),
								.o_Write_Address(),
								.o_Write_CLK    ()
							  );		
	
filter media_tops_u2(
								.i_clk          (CAMERA_PIXCLK), 
								.i_rst          (~DLY_RST_1), 
								.i_din          (sCCD_G_), 
								.o_address      (), 
								.o_enable0      (),//NC 
								.o_median       (sCCD_G0), 
								.o_enable1      (),//NC 
								.o_frame_flag   (),
								.o_Write_Address(),
								.o_Write_CLK    ()
							  );	
	
filter media_tops_u3(
								.i_clk          (CAMERA_PIXCLK), 
								.i_rst          (~DLY_RST_1), 
								.i_din          (sCCD_B_), 
								.o_address      (), 
								.o_enable0      (),//NC 
								.o_median       (sCCD_B0), 
								.o_enable1      (),//NC 
								.o_frame_flag   (),
								.o_Write_Address(),
								.o_Write_CLK    ()
							  );	
	
//彩色图,转换为灰度图,Y = 0.3R + 0.59G + 0.11B	
//彩色图,转换为灰度图,Y = 0.3R + 0.59G + 0.11B							
//彩色图,转换为灰度图,Y = 0.3R + 0.59G + 0.11B					
//0.3  = 1/4+1/32+1/64+1/512+1/1024			
//0.59 = 1/2+1/16+1/64+1/128+1/256
//0.11 = 1/16+1/32+1/64						
						
wire[11:0]R1;
wire[11:0]G1;					
wire[11:0]B1;						
wire[11:0]GRAY;						
//0.3  = 1/4+1/32+1/64+1/512+1/1024							
assign R1 = {sCCD_R0[11],sCCD_R0[11],sCCD_R0[11:2]} + {sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11:5]} + 
				{sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11:6]} + 	
				{sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11:9]} + 
				{sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11:10]};
				
//0.59 = 1/2+1/16+1/64+1/128+1/256				
assign G1 = {sCCD_G0[11],sCCD_G0[11:1]} + {sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11:4]} + 			
				{sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11:6]} + 
				{sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11:7]} + 
				{sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11:8]}; 
				
//0.11 = 1/16+1/32+1/64						
assign B1 = {sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11:4]} + 			
				{sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_G0[11:5]} + 
				{sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11:6]}; 

assign GRAY = R1+G1+B1;


//获得前后2帧,进行差分计算
//获得前后2帧,进行差分计算
//获得前后2帧,进行差分计算
wire[11:0]Frame1;
wire[11:0]Frame2;					

Delay_frame Delay_frame_u(
                         .i_clk   (CAMERA_PIXCLK),
								 .i_rst   (~DLY_RST_1),
								 .i_din   (GRAY),
								 .o_frame1(Frame1),
								 .o_frame2(Frame2)
	                     );
//差分
wire signed[11:0]Framediff;
assign Framediff = Frame1-Frame2;

wire signed[11:0]o_tracker;
tracker tracker_u(
                 .i_clk     (CAMERA_PIXCLK),
					  .i_rst     (~DLY_RST_1),
					  .i_data    (Framediff),
					  .o_tracker (o_tracker)
	             );


//基于SAD的模板匹配
//基于SAD的模板匹配
//基于SAD的模板匹配
 
wire[23:0]SADR;
SAD_tops1 SAD_tops1_u1(
    .i_clk             (CAMERA_PIXCLK), 
    .i_rst             (~DLY_RST_1), 
	 .i_en              (1'b1),
    .i_images          (sCCD_R0), 
    .i_images_en       (1'b1), 
    .o_SAD             (SADR),
	 .o_en              ()
    );

wire[23:0]SADG;
SAD_tops1 SAD_tops1_u2(
    .i_clk             (CAMERA_PIXCLK), 
    .i_rst             (~DLY_RST_1), 
	 .i_en              (1'b1),
    .i_images          (sCCD_G0), 
    .i_images_en       (1'b1), 
    .o_SAD             (SADG),
	 .o_en              ()
    );


wire[23:0]SADB;
SAD_tops1 SAD_tops1_u3(
    .i_clk             (CAMERA_PIXCLK), 
    .i_rst             (~DLY_RST_1), 
	 .i_en              (1'b1),
    .i_images          (sCCD_B0), 
    .i_images_en       (1'b1), 
    .o_SAD             (SADB),
	 .o_en              ()
    );	 
								
//显示跟踪到的目标的位置
//显示跟踪到的目标的位置
//显示跟踪到的目标的位置
wire[11:0]sCCD_R1;
wire[11:0]sCCD_G1;					
wire[11:0]sCCD_B1;
disp disp_u(
            .i_clk  (CAMERA_PIXCLK),
				.i_rst  (~DLY_RST_1),
				.i_track(o_tracker),
				.i_sadR (SADR),
				.i_sadG (SADG),
				.i_sadB (SADB),
				.i_R    (sCCD_R0),
				.i_G    (sCCD_G0),
				.i_B    (sCCD_B0),
				.o_R    (sCCD_R1),
				.o_G    (sCCD_G1),
				.o_B    (sCCD_B1)
	        );
								
								

assign sCCD_R = sCCD_R1;
assign sCCD_G = sCCD_G1;
assign sCCD_B = sCCD_B1;

 
								
//frame number display
SEG7_LUT_8 			u5	(	.oSEG0(HEX0),.oSEG1(HEX1),
							.oSEG2(HEX2),.oSEG3(HEX3),
							.oSEG4(HEX4),.oSEG5(HEX5),
							.oSEG6(HEX6),.oSEG7(HEX7),
							.iDIG(Frame_Cont[31:0])
						);
//pll
sdram_pll 			u6	(
							.inclk0(CLOCK2_50),
							.c0(sdram_ctrl_clk),
							.c1(DRAM_CLK),
							.c2(CAMERA_XCLKIN), // cmos sensor main clock input,25M
							.c3(lcd_clk) // cmos sensor lcd pix clock,33M
						);
//frame buffer
Sdram_Control	u7	(	//	HOST Side						
						    .RESET_N(KEY[0]),
							.CLK(sdram_ctrl_clk),

							//	FIFO Write Side 1
							.WR1_DATA({1'b0,sCCD_G[11:7],sCCD_B[11:2]}),
							.WR1(sCCD_DVAL),
							.WR1_ADDR(0),
							.WR1_MAX_ADDR(800*480/2),
							.WR1_LENGTH(8'h80),
							.WR1_LOAD(!DLY_RST_0),
							.WR1_CLK(CAMERA_PIXCLK),

							//	FIFO Write Side 2
							.WR2_DATA(	{1'b0,sCCD_G[6:2],sCCD_R[11:2]}),
							//.WR2_DATA(	{6'b000000,10'b1111111111}),
							.WR2(sCCD_DVAL),
							.WR2_ADDR(23'h100000),
							.WR2_MAX_ADDR(23'h100000+800*480/2),
							.WR2_LENGTH(8'h80),
							.WR2_LOAD(!DLY_RST_0),
							.WR2_CLK(CAMERA_PIXCLK),


							//	FIFO Read Side 1
						    .RD1_DATA(Read_DATA1),
				        	.RD1(Read),
				        	.RD1_ADDR(0),
							.RD1_MAX_ADDR(800*480/2),
							.RD1_LENGTH(8'h80),
							.RD1_LOAD(!DLY_RST_0),
							.RD1_CLK(~LTP_CTRL_CLK),
							
							//	FIFO Read Side 2
						    .RD2_DATA(Read_DATA2),
							.RD2(Read),
							.RD2_ADDR(23'h100000),
							.RD2_MAX_ADDR(23'h100000+800*480/2),
							.RD2_LENGTH(8'h80),
				        	.RD2_LOAD(!DLY_RST_0),
							.RD2_CLK(~LTP_CTRL_CLK),
							
							//	SDRAM Side
						   .SA(DRAM_ADDR),
							.BA(DRAM_BA),
							.CS_N(DRAM_CS_N),
							.CKE(DRAM_CKE),
							.RAS_N(DRAM_RAS_N),
							.CAS_N(DRAM_CAS_N),
							.WE_N(DRAM_WE_N),
							.DQ(DRAM_DQ),
							.DQM(DRAM_DQM)
						);
//cmos sensor configuration
I2C_CCD_Config 		u8	(	//	Host Side
							.iCLK(CLOCK2_50),
							.iRST_N(DLY_RST_2),
							.iEXPOSURE_ADJ(KEY[1]),
							.iEXPOSURE_DEC_p(SW[0]),
							.iMIRROR_SW(SW[17]),
							//	I2C Side
							.I2C_SCLK(CAMERA_SCLK),
							.I2C_SDAT(CAMERA_SDATA)
						);
//ltp controller
ltp_controller		u1	( 	.iCLK(LTP_CTRL_CLK),
							.iRST_n(DLY_RST_2),
							// sdram side
							.iREAD_DATA1(Read_DATA1),
							.iREAD_DATA2(Read_DATA2),
							.oREAD_SDRAM_EN(Read),
							// lcd side
							.oLCD_R(lcd_r),
							.oLCD_G(lcd_g),
							.oLCD_B(lcd_b),
							.oHD(lcd_hs),
							.oVD(lcd_vs),
							.oDEN()
							);

4.FPGA测试

可以单独做modelsim仿真

的modelsim仿真如下所示:

oframe2为延迟一帧后的视频,然后oframe1和oframe2就可以做差分了。

的modelsim仿真如下所示:

将该仿真图放大,可以看到:

由于中值滤波,是将3*3窗口内的像素值,取其中间值作为滤波输出,所以滤波后的图像像素数据o_median相对于输入像素i_din,码率上为原来的1/9。其中o_write_CLK为输出像素值o_median所对应的时钟频率。

的modelsim仿真如下所示:

的modelsim仿真如下所示:

对于满足阈值范围的,o_tacker取值1,否则取值0.

A10-48

猜你喜欢

转载自blog.csdn.net/ccsss22/article/details/125784557