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

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。

FPGA视觉从入门到放弃——Canny算子_第1张图片

问题:均值滤波的对应原理?

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算子 + “腐蚀”的边缘提取结果。

FPGA视觉从入门到放弃——Canny算子_第2张图片


你可能感兴趣的:(FPGA,Vision)