(3) 求得 Gx^2+Gy^2 的结果, 及 Gx 与 Gy 的平方和
这一步直接通过 HDL 中乘法器的描述来实现, 综合时会自动布线为片内乘法器,如下:
代码8‑2 Gx 与 Gy 的平方和
1. //--------------------------------------- 2. //Caculate the square of distance = (Gx^2 + Gy^2) 3. //Step 3 4. reg [20:0] Gxy_square; 5. always@(posedge clk or negedge rst_n) 6. begin 7. if(!rst_n) 8. Gxy_square <= 0; 9. else 10. Gxy_square <= Gx_data * Gx_data + Gy_data * Gy_data; 11.end |
---|
(4) 求得 Gx^2+Gy^2 的平方根
一、Quartus II中可以直接使用平方根IP值
强大的 Altera 在 LPM 中提供了平方根的 IP,如下所示:
图8‑8 LPM 中提供了平方根的 IP
ALTSQRT 的使用非常简单,以下直接给出 SQRT 的例化,从而得到平方根, 如下:
//---------------------------------------
//Caculate the distance of P5 =(Gx^2 + Gy^2)^0.5
//Step 4
wire [10:0] Dim;
SQRT u_SQRT
(
.radical (Gxy_square),
.q (Dim),
.remainder ()
);
二、Vivado中可以进行开方操作
Vivado中没有相关的直接开方IP可以使用,但是可以使用更方便的CORDIC实现开方操作,详细的IP使用如下:
图8‑9添加IP
设置IP如下,具体如下:
图8‑10 CORDICIP调用界面设置
左边的Tab可以切换看到CORDIC的模块图(IP Symbol)、实现细节(Implementation Details)。其中最重要的信息就是Latency,由这个值可以知道得到输出结果需要多少个时钟。右边的Tab是对CORDIC进行配置。
FunctionalSelection选择为“SquareRoot”,结构默认为并行结构“Parallel”。Pipelining Mode可以设置为最大值(Maximum)、最优值(Optimal)和不设置流水线(No pipelining即纯组合逻辑实现),增加流水线级数可以提高计算速度。计算SquareRoot时Data Format固定为不带符号整数(Unsigned Intelger)。Phase Format可以设置为Radians(以pi为单位)或Scaled Radians(将单位pi归一化到-1~1范围内)。
Input/OutputOptions中设置输入数据位宽和输出数据位宽,以及舍位的模式,这里选择为Nearest Even,表示最接近的值,其他含义如下。
第一种:Truncate:直接取整
第二种:Round Pos Inf:四舍五入,+0.5之后四舍五入,在负数时和第三种有区别。
第三种:Round Pos Neg Inf:四舍五入
第四种:四舍五入,0.5算舍去。
图8‑11舍位模式含义
还要注意一下输入输出位宽,官方的解释如下:
图8‑12输入输出模式
另外值得注意的一个参数是“Coarse Rotation”,默认为勾选。选中此值时,CORDIC的输出范围是-pi~pi;没有选中时,CORDIC的输出范围是-1/4pi~1/4pi。
Vivado的很多IP核采用的是AXI4接口,主要有数据(tdata)、准备好(tready)、有效(tvalid)几种信号,还有主机(m)和从机(s)之分。另外在AXI4 Stream Options这个Tab还可以配置使用更多辅助的AXI4接口信号。
接下来介绍几个主要的接口:
表8‑1 AXI4接口信号
名称 | 方向 | 说明 |
---|---|---|
aclk | 输入 | 时钟信号 |
aresetn | 输入 | 低电平有效复位信号 |
s_axis_cartesian_tvalid | 输入 | 输入数据有效信号 |
s_axis_cartesian_tdata | 输入 | CORDIC输入数据总线 |
m_axis_data_tvalid | 输出 | 输出数据有效信号 |
m_axis_data_tdata | 输出 | CORDIC输出数据总线 |
上表中的接口在计算Square Root时所需。其它接口在后面的设计中使用到CORDIC的其它功能时,再做介绍。需要注意,AXI4接口位宽是以字节为单位,即只会是8的倍数,因此需要结合设计的实际位宽做相应处理。
调用代码如下:
代码8‑3例化CORDIC “Square Root”代码
1. //--------------------------------------- 2. //Caculate the distance of P5 = (Gx^2 + Gy^2)^0.5 3. //Step 4 4. wire [10:0] Dim; 5. wire outvalid; 6. XILSQRT u_SQRT 7. ( 8. .aclk (clk), 9. .aresetn (rst_n), 10. .s_axis_cartesian_tvalid (1'b1), 11. .s_axis_cartesian_tdata (Gxy_square), 12. .m_axis_dout_tvalid (outvalid), 13. .m_axis_dout_tdata (Dim) 14. 15. ); |
---|
第二种方式的实现方法其实就很简单了,只需要将第一、二步得到的计算结果进行相加即可得到该点的灰度值,所以这里就不详细介绍了,具体看下源码即可。
根据外部输入阀值,判断并实现边缘的检测
简单的判断 Dim 的大小而已,大于阀值,视为有效,赋 1; 反之则赋 0。具体看代码吧。
代码8‑4
1. //--------------------------------------- 2. //Compare and get the Sobel_data 3. //Step 5 4. reg post_img_Bit_r; 5. always@(posedge clk or negedge rst_n) 6. begin 7. if(!rst_n) 8. post_img_Bit_r <= 1'b0; //Default None 9. else if(Dim >= Sobel_Threshold) 10. post_img_Bit_r <= 1'b1; //Edge Flag 11. else 12. post_img_Bit_r <= 1'b0; //Not Edge 13.end |
---|
这里说明一下,Sobel_Threshold这个值是可以通过外部按键进行加减以实现动态阈值的调节,实现难度不大,具体可以看下代码。
前面从(1) -(5)中,总共消耗了 5 个时钟, 因此最后对行场、 像素有效时钟进行 5 个 clock 的偏移。
这样,便完成了 Soble 边缘检测算法的移植。ModelSim中的仿真波形如下:
图8‑13 ModelSim中的仿真波形
实验结果如下:
图8‑14实验结果
《摘抄自网上》
Lena 图像, 在阀值=90 下 RGB→Gray→Sobel 的图像如下所示:
图8‑15图像实例
相对更简单的还有一种所谓 Robert 的边缘检测算法,只需要 4 个像素。卷积因子如下所示:
图8‑16 Robert的边缘检测算子
该算法在 Matlab 下验证 Lena 真身,在阀值 =30下,得到的RGB→Gray→Robert 如下图所示:
图8‑17 Robert算法实例
对比 Sobel 与 Robert,得到的边缘检测对比图如下:
图8‑18 Sobel与 Robert对比
左图为 Sobel 实现,得到了更多的细节;右图为 Robert 实现, 相对 Sobel 实现更简单。
此外典型的边缘检测算法还有 Canny 等, 这里不做过多的介绍,相关的资料博文如下:
https://blog.csdn.net/xiaowei_cqu/article/details/7839140
https://www.cnblogs.com/cfantaisie/archive/2011/06/05/2073168.html