DSP

FPGA视觉从入门到放弃——Canny算子

2019-07-13 18:54发布

FPGA视觉从入门到放弃——Canny算子


一. FPGA视觉从入门到放弃简介


本笔记仅适合实验室内部的FPGA图像采集卡,并不适合计算机视觉中的高大上场合,但功耗和速度很阔怕,同时方法简单得阔怕。毕竟没有什么方法或工具可以永远时尚下去,所以从入门到放弃也算是进步之选嘛~

二. Prefix


1.常用变量术语


CLK         时钟20ns LLC         经过锁相环的时钟37ns oddfield   采集奇场图像时输出高电平 Y_flag     与像素灰度值同步的高电平脉冲 m2_***    wishbone总线的控制信号与数据信号

2.贴士


reg默认为无符号,有符号为reg signed; Camera_AB.v模块为最高层模块; FIFO数据达到16个,准备写SRAM;写完SRAM后从IDLE分支调到WRITE_END分支,主动放弃总线。

3.FPGA切换SRAM访问权

SRAM_A和B有容量512K=2^9*2^10=2^19,SAVEADDR为19位地址线;数据线16位宽。   FPGA先给SRAM_A写当前帧结果,DSP从SRAM_B读上一帧结果;下一时刻,FPGA再给SRAM_A写当前帧结果,DSP从SRAM_A读上一帧结果。   DSP的地址空间CE2设置为SRAM,空间地址0xA0000000开始;CE3设置为FPGA内部寄存器,空间地址0xB0000000开始。DSP与SRAM导线连接,读写更快。 FPGA_A_DSP_B为高电平时,FPGA访问SRAM_A,DSP访问SRAM_B。低电平时切换。

4.DSP读写CE3数据


(1)DSP读 DSP_nCE3为低电平且DSP_nRE(或DSP_nOE)为高电平时,FPGA内部16位导线dsp_data_read_ce3与DSP_DATA双向端口连接。   DSP_ADDR的地址为8位,DSPFPGA内部参数最多为256个字节,这样只会用到CE3空间很小的一部分。   (2)DSP写 DSP_nCE3为低电平且DSP_nWE为高电平时,取出DSP_ADDR地址处的DSP_DATA写入FPGA内部参数。

三. FPGA代码分析——Canny算子


问题:大小为720*576的图像在哪里降采样为360*288的? 答:原图像大小为720*576,odd_field和even_field同时存在时为该分辨率;只用odd_field或even_field则为360*288。  

1.Canny算子相对于高斯滤波在顶层文件Camera_AB.v中区别


Canny算子的sysreg0中多出1条8位线threshold_lower_limit,“为防止白纸上检测到无数边缘点”。 除了save_Y模块,还有save_Cb和save_Cr模块。 wishbone总线的master 7未用。   (1)Canny: 添加13位线G_VPO_Y; Gaussian_filter_Y模块滤波(726个Y_flags),模块输入为原图像VPO_out,输出为G_VPO_Y。 添加1位线max_flag; 添加8位线gradient; Sobel_Y模块提取梯度极大值(729个Y_flags),模块输入为Gaussian_filter_Y模块的输出G_VPO_Y,输出为max_flag和gradient。   添加17位线ram_read_data和ram_write_data; 添加1位线ram_write_enable; 添加8位线ram_addr; RAM_17bits_256depth模块建立Histogramof the Gradients(?),输入ram_write_data和ram_write_enable,输出ram_read_data。   //============================================================================= compare_gradient_count模块输入为Sobel模块的输出max_flag和gradient,以及RAM_17bits_256depth模块的输入输出。强边缘横坐标,强边缘纵坐标,弱边缘横坐标,弱边缘纵坐标分别占用master 3,4,5,6。   SAVEADDR_ROW_X为19'h20000,FPGA的SAVEADDR_***地址的2倍为DSP中对应的SAVEADDR_***地址。同理SAVEADDR_COL_Y为19'h28000,DSP地址为0x28000*2=0x50000,即SAVEADDR_STRONG_COL_Y。地址赋给mi_addr(i=3,4,5,6)。   默认高阈值high_threshold(8位reg)为240,最大强弱边缘像素数为32768。当梯度大于高阈值时,则记录为强边缘坐标;当梯度小于高阈值且大于高阈值的1/4时,则记录为弱边缘坐标。高阈值可以用DSP修改,先送给threshold_lower_limit。 //=============================================================================   添加8位线VPO_Y; median_filter_Y模块(731个时钟脉冲)输入为VPO_out,输出为VPO_Y。   //============================================================================= compare_Y_count模块用Y阈值比较像素值,保存二值化图像中的每个“亮”像素。输入为VPO_Y和Y_threshold,输出bright_pix_num,又向wishbone总线的master3,4写入强边缘横坐标和强边缘纵坐标。   默认Y阈值DEFAULT_Y_THRESHOLD为240。 //=============================================================================

问题:中值滤波的输入图像是原图像而不是gradient,滤波结果与边缘提取没关系吧?再写1m3_data_writem4_data_write是为什么? 已经注释掉了中值滤波的部分。   (2)Gaussian: 添加8位线VPO_median_Y;有不同大小高斯模板的滤波模块。

Interrupt the DSP和wishbone总线部分没变。保存原图像都占用master 0。

四. DSP代码分析——Canny算子


获得强边缘的坐标存入堆栈数组,在强边缘的8邻域内搜索弱边缘。如果存在弱边缘,将弱边缘修改为强边缘,完成强弱边缘的连接。
注:DSP每个地址的数据类型为unsigned int 16。

1. clear_Y_data()


Y_data和Label清0。

2. get_the_edge_points()


(1) 强边缘 STRONG_EDGE_PIX_NUM_ADDR为0xB0000062,从该地址取边缘像素数edge_pix_num; MAX_EDGE_PIX_NUM为32768,用于对edge_pix_num限幅。 SAVEADDR_STRONG_ROW_X为0xA0040000,从该地址提取强边缘的横坐标; SAVEADDR_STRONG_COL_Y为0xA0050000,从该地址提取强边缘的纵坐标。 如果4<横坐标x<285且4<纵坐标y<357,则Y_data在该点的像素值为255,实际边缘像素数actual_edge_num加1,把x-1和y-1存入栈数组stack_row_x和stack_row_y。 strong_edge_pix_num记录DSP从强边缘的存储地址中提取的横纵坐标位于图像内的有效像素数。   注:强边缘坐标最多有0x10000=65536个坐标,图像大小为360*288=103680个坐标。强边缘像素数占8个字节,感觉3个字节就可以。
  (2) 弱边缘 弱边缘同理。WEAK_EDGE_PIX_NUM_ADDR为0xB0000066; SAVEADDR_WEAK_ROW_X为0xA0060000; SAVEADDR_WEAK_COL_Y为0xA0070000。 如果满足边界条件,则Y_data在该点的像素值为1。并未存入栈数组。 weak_edge_pix_num记录有效的弱边缘像素数。
  (3) 连接边缘 索引从强边缘坐标数组的末端开始往前扫描。假设存在强边缘坐标(x0,y0)(Y_data在该点处的像素值为255)。寻找强边缘坐标的8近邻区域是否存在弱边缘坐标(x0+x1,y0+y1)(Y_data在该点处的像素值为1),如果存在,则该相邻点坐标(x0+x1,y0+y1)处的像素值改为255,同时实际边缘数加1,坐标(x0+x1,y0+y1)存入栈数组。

五.主机与DSP通信——Canny算子


1.发送边缘阈值


51 0xFF value_edge_threshold U8_data[0]=0x00U8_data[1]=0x00

2.读原图像


m_original->imageData的大小为1080*1080,选择前96*1080字节:360*288=103680=96*1082。

六. FPGA代码分析——腐蚀


1.腐蚀相对于Canny算子在顶层文件Camera_AB.v中区别


LLC,VSYNC,FID,VPO后缀增加_S:LLC->LLC_S。 master 5,6,7未用,而Canny算子只有master 7未用。

//============================================================================= Average_filter_Y_f模块同时选择oddfield和evenfield,输入图像VPO_out,输出滤波结果A_VPO_Y_f。   taps为2位移位寄存器的水龙头输出(8位线),输入为VPO_in。


问题:均值滤波的对应原理? Y11p<-Y12p<-Y13p Y31p<-Y31p<-Y32p taps缓存图像序列VPO_in上一行位置的值。
我被文件标题欺骗:这里不是腐蚀,是2*3均值滤波。所以后面的腐蚀带双引号~ //=============================================================================

compare_Y_count模块输入为VPO_in;Canny对应VPO_Y,增加bright_pix_num。

七. DSP代码分析——“腐蚀”


虽然FPGA处理的图像大小为720*576,get_original_image依然获得大小为360*288的图像。get_laser_image应该是获取结果图像,Y_even_buffer地址为0xA0040000;Y_odd_buffer地址为0xA0046540=25920(字节)。   灰度图像大小为720*576=414720(字节),奇场图像为一半:414720/2=207360(字节)<25920(字节)

八. FPGA代码制作——Canny算子 + “腐蚀”


1.复制Average_filter_Y_f.v,taps_2_lines.bsf/.qip/.v至src文件夹下; 2.在Camera_AB中添加模块Average_filter_Y,输入VPO_in为gradient,Y_f_flag改为Y_flag; 3.修改compare_gradient_count中的输入为A_VPO_Y_f。

九. 实验结果


左图为Canny算子的边缘提取结果;右图为Canny算子 + “腐蚀”的边缘提取结果。