• 【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.核心程序

    1. //reset signals
    2. Reset_Delay u2 ( .iCLK(CLOCK2_50),
    3. .iRST(KEY[0]),
    4. .oRST_0(DLY_RST_0),
    5. .oRST_1(DLY_RST_1),
    6. .oRST_2(DLY_RST_2),
    7. .oRST_3(DLY_RST_3), //auto-start start point
    8. .oRST_4(DLY_RST_4) //auto-start end point
    9. );
    10. //cmos sensor capture
    11. CCD_Capture u3 ( .oDATA(mCCD_DATA),
    12. .oDVAL(mCCD_DVAL),
    13. .oX_Cont(X_Cont),
    14. .oY_Cont(Y_Cont),
    15. .oFrame_Cont(Frame_Cont),
    16. .iDATA(rCCD_DATA),
    17. .iFVAL(rCCD_FVAL),
    18. .iLVAL(rCCD_LVAL),
    19. .iSTART((!KEY[3])| auto_start),
    20. .iEND(!KEY[2]),
    21. .iCLK(~CAMERA_PIXCLK),
    22. .iRST(DLY_RST_2)
    23. );
    24. //raw data to RGB conversion
    25. wire [11:0] sCCD_R_;
    26. wire [11:0] sCCD_G_;
    27. wire [11:0] sCCD_B_;
    28. RAW2RGB u4 ( .iCLK(CAMERA_PIXCLK),
    29. .iRST_n(DLY_RST_1),
    30. .iData(mCCD_DATA),
    31. .iDval(mCCD_DVAL),
    32. .oRed(sCCD_R_),
    33. .oGreen(sCCD_G_),
    34. .oBlue(sCCD_B_),
    35. .oDval(sCCD_DVAL),
    36. .iMIRROR(SW[17]),
    37. .iX_Cont(X_Cont),
    38. .iY_Cont(Y_Cont)
    39. );
    40. //图像的中值滤波
    41. //图像的中值滤波
    42. //图像的中值滤波
    43. wire [11:0] sCCD_R0;
    44. wire [11:0] sCCD_G0;
    45. wire [11:0] sCCD_B0;
    46. filter media_tops_u1(
    47. .i_clk (CAMERA_PIXCLK),
    48. .i_rst (~DLY_RST_1),
    49. .i_din (sCCD_R_),
    50. .o_address (),
    51. .o_enable0 (),//NC
    52. .o_median (sCCD_R0),
    53. .o_enable1 (),//NC
    54. .o_frame_flag (),
    55. .o_Write_Address(),
    56. .o_Write_CLK ()
    57. );
    58. filter media_tops_u2(
    59. .i_clk (CAMERA_PIXCLK),
    60. .i_rst (~DLY_RST_1),
    61. .i_din (sCCD_G_),
    62. .o_address (),
    63. .o_enable0 (),//NC
    64. .o_median (sCCD_G0),
    65. .o_enable1 (),//NC
    66. .o_frame_flag (),
    67. .o_Write_Address(),
    68. .o_Write_CLK ()
    69. );
    70. filter media_tops_u3(
    71. .i_clk (CAMERA_PIXCLK),
    72. .i_rst (~DLY_RST_1),
    73. .i_din (sCCD_B_),
    74. .o_address (),
    75. .o_enable0 (),//NC
    76. .o_median (sCCD_B0),
    77. .o_enable1 (),//NC
    78. .o_frame_flag (),
    79. .o_Write_Address(),
    80. .o_Write_CLK ()
    81. );
    82. //彩色图,转换为灰度图,Y = 0.3R + 0.59G + 0.11B
    83. //彩色图,转换为灰度图,Y = 0.3R + 0.59G + 0.11B
    84. //彩色图,转换为灰度图,Y = 0.3R + 0.59G + 0.11B
    85. //0.3 = 1/4+1/32+1/64+1/512+1/1024
    86. //0.59 = 1/2+1/16+1/64+1/128+1/256
    87. //0.11 = 1/16+1/32+1/64
    88. wire[11:0]R1;
    89. wire[11:0]G1;
    90. wire[11:0]B1;
    91. wire[11:0]GRAY;
    92. //0.3 = 1/4+1/32+1/64+1/512+1/1024
    93. 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]} +
    94. {sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11],sCCD_R0[11:6]} +
    95. {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]} +
    96. {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]};
    97. //0.59 = 1/2+1/16+1/64+1/128+1/256
    98. 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]} +
    99. {sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11],sCCD_G0[11:6]} +
    100. {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]} +
    101. {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]};
    102. //0.11 = 1/16+1/32+1/64
    103. assign B1 = {sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11:4]} +
    104. {sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_G0[11:5]} +
    105. {sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11],sCCD_B0[11:6]};
    106. assign GRAY = R1+G1+B1;
    107. //获得前后2帧,进行差分计算
    108. //获得前后2帧,进行差分计算
    109. //获得前后2帧,进行差分计算
    110. wire[11:0]Frame1;
    111. wire[11:0]Frame2;
    112. Delay_frame Delay_frame_u(
    113. .i_clk (CAMERA_PIXCLK),
    114. .i_rst (~DLY_RST_1),
    115. .i_din (GRAY),
    116. .o_frame1(Frame1),
    117. .o_frame2(Frame2)
    118. );
    119. //差分
    120. wire signed[11:0]Framediff;
    121. assign Framediff = Frame1-Frame2;
    122. wire signed[11:0]o_tracker;
    123. tracker tracker_u(
    124. .i_clk (CAMERA_PIXCLK),
    125. .i_rst (~DLY_RST_1),
    126. .i_data (Framediff),
    127. .o_tracker (o_tracker)
    128. );
    129. //基于SAD的模板匹配
    130. //基于SAD的模板匹配
    131. //基于SAD的模板匹配
    132. wire[23:0]SADR;
    133. SAD_tops1 SAD_tops1_u1(
    134. .i_clk (CAMERA_PIXCLK),
    135. .i_rst (~DLY_RST_1),
    136. .i_en (1'b1),
    137. .i_images (sCCD_R0),
    138. .i_images_en (1'b1),
    139. .o_SAD (SADR),
    140. .o_en ()
    141. );
    142. wire[23:0]SADG;
    143. SAD_tops1 SAD_tops1_u2(
    144. .i_clk (CAMERA_PIXCLK),
    145. .i_rst (~DLY_RST_1),
    146. .i_en (1'b1),
    147. .i_images (sCCD_G0),
    148. .i_images_en (1'b1),
    149. .o_SAD (SADG),
    150. .o_en ()
    151. );
    152. wire[23:0]SADB;
    153. SAD_tops1 SAD_tops1_u3(
    154. .i_clk (CAMERA_PIXCLK),
    155. .i_rst (~DLY_RST_1),
    156. .i_en (1'b1),
    157. .i_images (sCCD_B0),
    158. .i_images_en (1'b1),
    159. .o_SAD (SADB),
    160. .o_en ()
    161. );
    162. //显示跟踪到的目标的位置
    163. //显示跟踪到的目标的位置
    164. //显示跟踪到的目标的位置
    165. wire[11:0]sCCD_R1;
    166. wire[11:0]sCCD_G1;
    167. wire[11:0]sCCD_B1;
    168. disp disp_u(
    169. .i_clk (CAMERA_PIXCLK),
    170. .i_rst (~DLY_RST_1),
    171. .i_track(o_tracker),
    172. .i_sadR (SADR),
    173. .i_sadG (SADG),
    174. .i_sadB (SADB),
    175. .i_R (sCCD_R0),
    176. .i_G (sCCD_G0),
    177. .i_B (sCCD_B0),
    178. .o_R (sCCD_R1),
    179. .o_G (sCCD_G1),
    180. .o_B (sCCD_B1)
    181. );
    182. assign sCCD_R = sCCD_R1;
    183. assign sCCD_G = sCCD_G1;
    184. assign sCCD_B = sCCD_B1;
    185. //frame number display
    186. SEG7_LUT_8 u5 ( .oSEG0(HEX0),.oSEG1(HEX1),
    187. .oSEG2(HEX2),.oSEG3(HEX3),
    188. .oSEG4(HEX4),.oSEG5(HEX5),
    189. .oSEG6(HEX6),.oSEG7(HEX7),
    190. .iDIG(Frame_Cont[31:0])
    191. );
    192. //pll
    193. sdram_pll u6 (
    194. .inclk0(CLOCK2_50),
    195. .c0(sdram_ctrl_clk),
    196. .c1(DRAM_CLK),
    197. .c2(CAMERA_XCLKIN), // cmos sensor main clock input,25M
    198. .c3(lcd_clk) // cmos sensor lcd pix clock,33M
    199. );
    200. //frame buffer
    201. Sdram_Control u7 ( // HOST Side
    202. .RESET_N(KEY[0]),
    203. .CLK(sdram_ctrl_clk),
    204. // FIFO Write Side 1
    205. .WR1_DATA({1'b0,sCCD_G[11:7],sCCD_B[11:2]}),
    206. .WR1(sCCD_DVAL),
    207. .WR1_ADDR(0),
    208. .WR1_MAX_ADDR(800*480/2),
    209. .WR1_LENGTH(8'h80),
    210. .WR1_LOAD(!DLY_RST_0),
    211. .WR1_CLK(CAMERA_PIXCLK),
    212. // FIFO Write Side 2
    213. .WR2_DATA( {1'b0,sCCD_G[6:2],sCCD_R[11:2]}),
    214. //.WR2_DATA( {6'b000000,10'b1111111111}),
    215. .WR2(sCCD_DVAL),
    216. .WR2_ADDR(23'h100000),
    217. .WR2_MAX_ADDR(23'h100000+800*480/2),
    218. .WR2_LENGTH(8'h80),
    219. .WR2_LOAD(!DLY_RST_0),
    220. .WR2_CLK(CAMERA_PIXCLK),
    221. // FIFO Read Side 1
    222. .RD1_DATA(Read_DATA1),
    223. .RD1(Read),
    224. .RD1_ADDR(0),
    225. .RD1_MAX_ADDR(800*480/2),
    226. .RD1_LENGTH(8'h80),
    227. .RD1_LOAD(!DLY_RST_0),
    228. .RD1_CLK(~LTP_CTRL_CLK),
    229. // FIFO Read Side 2
    230. .RD2_DATA(Read_DATA2),
    231. .RD2(Read),
    232. .RD2_ADDR(23'h100000),
    233. .RD2_MAX_ADDR(23'h100000+800*480/2),
    234. .RD2_LENGTH(8'h80),
    235. .RD2_LOAD(!DLY_RST_0),
    236. .RD2_CLK(~LTP_CTRL_CLK),
    237. // SDRAM Side
    238. .SA(DRAM_ADDR),
    239. .BA(DRAM_BA),
    240. .CS_N(DRAM_CS_N),
    241. .CKE(DRAM_CKE),
    242. .RAS_N(DRAM_RAS_N),
    243. .CAS_N(DRAM_CAS_N),
    244. .WE_N(DRAM_WE_N),
    245. .DQ(DRAM_DQ),
    246. .DQM(DRAM_DQM)
    247. );
    248. //cmos sensor configuration
    249. I2C_CCD_Config u8 ( // Host Side
    250. .iCLK(CLOCK2_50),
    251. .iRST_N(DLY_RST_2),
    252. .iEXPOSURE_ADJ(KEY[1]),
    253. .iEXPOSURE_DEC_p(SW[0]),
    254. .iMIRROR_SW(SW[17]),
    255. // I2C Side
    256. .I2C_SCLK(CAMERA_SCLK),
    257. .I2C_SDAT(CAMERA_SDATA)
    258. );
    259. //ltp controller
    260. ltp_controller u1 ( .iCLK(LTP_CTRL_CLK),
    261. .iRST_n(DLY_RST_2),
    262. // sdram side
    263. .iREAD_DATA1(Read_DATA1),
    264. .iREAD_DATA2(Read_DATA2),
    265. .oREAD_SDRAM_EN(Read),
    266. // lcd side
    267. .oLCD_R(lcd_r),
    268. .oLCD_G(lcd_g),
    269. .oLCD_B(lcd_b),
    270. .oHD(lcd_hs),
    271. .oVD(lcd_vs),
    272. .oDEN()
    273. );

    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

  • 相关阅读:
    Godot 官方2D游戏笔记(1):导入动画资源和添加节点
    53. 最大子数组和
    MyBatis-Plus整合(二)
    写给Python社群的第10课:Python 研发 Windows桌面程序,长长长的文
    CSRF 跨站请求伪造
    MariaDB简介
    一文搞懂 连接池,线程池,内存池,异步请求池 ,学会实现异步效果
    免root修改手机imei的技术原理是什么?如何实现的?hook吗
    springboot整合pi支付开发
    Vue向pdf文件中添加二维码
  • 原文地址:https://blog.csdn.net/ccsss22/article/details/125784557