赞
踩
1.窗口缓存模块win_buf为了验证本模块的正确性,我们生成一幅256×256的渐变图,以 这个渐变图的3×3窗口缓存为例来进行验证。这个渐变图的像素值有 以下定义:
- for (int i = 0; i < dwHeight; i++)
- for (int j = 0; j < dwWidth; j++)
- {
- pBitmap[i*dwWidth + j]= i+j;
- }
即 图像的每一行均是渐变的,但是初始像素值是当前行数,该图 像如图10-18所示。
- 设计测试代码如下:
- wire [local_dw-1:0]win_buf_din;
- wire win_buf_vsync;
- wire win_buf_dvalid;
- wire [local_dw-1:0]win_buf_data_org;wire [local_dw-1:0]win_buf_new_data_org;
- wire win_buf_is_boarder;
- wire win_buf_new_is_boarder;
- wire win_buf_out_valid;
- wire win_buf_new_out_valid;
- wire [local_dw*3*3-1:0]win_buf_new_data_out;
- wire [local_dw-1:0]test[0:8];/*便于查看测试结果*/
- assign win_buf_dvalid = cap_dvalid ;
- assign win_buf_din = cap_data;
- assign win_buf_vsync = cap_vsync;
- win_buf win_buf_ins(
- .rst_n(reset_l),
- .clk(cap_clk),
- .din_valid(win_buf_dvalid),
- .din(win_buf_din),
- .dout(win_buf_new_data_out), //output vector
- .dout_org(win_buf_data_org), //the centor of the
- window
- .vsync(win_buf_vsync),
- .vsync_out(),
- .is_boarder(win_buf_is_boarder), //boarder
- information
- .dout_valid(win_buf_out_valid)
- );
- defparam win_buf_ins.DW = local_dw;
- defparam win_buf_ins.KSZ = 3;
- defparam win_buf_ins.IH = ih;defparam win_buf_ins.IW = iw;
- assign test[0]= win_buf_new_data_out[local_dw-1:0];
- assign test[1]= win_buf_new_data_out[2*local_dw-
- 1:local_dw];
- assign test[2]= win_buf_new_data_out[3*local_dw-
- 1:2*local_dw];
- assign test[3]= win_buf_new_data_out[4*local_dw-
- 1:3*local_dw];
- assign test[4]= win_buf_new_data_out[5*local_dw-
- 1:4*local_dw];
- assign test[5]= win_buf_new_data_out[6*local_dw-
- 1:5*local_dw];
- assign test[6]= win_buf_new_data_out[7*local_dw-
- 1:6*local_dw];
- assign test[7]= win_buf_new_data_out[8*local_dw-
- 1:7*local_dw];
- assign test[8]= win_buf_new_data_out[9*local_dw-
- 1:8*local_dw];
我们列出该图片前6行数据,见表10-1。
可以预见的是,中心像素值从第二行第二列即第二行的高亮处开 始有效,在图像的非边缘区域以流水方式移动,即dout_org的值为2,3,4,5,6,7,…,见表10-2。
同时我们也可以预知输出向量,即输出窗口缓存test的前几个有 效输出,见表10-3。
我们也可以得到在表10-1中的第3行第3列输出后才能得到test的 第一个有效输出,这是由于在这个时钟时候才能得到第一个完整的 3×3窗口。 截取前面几个有效输出时钟如下:
仿真图也验证了我们的猜想,说明我们的设计逻辑正确。 2.数据累加模块add_tree对于本模块的测试,我们提供两个测试实例:一个测试实例计算 从1加到100的和,另一个测试实例生成7个随机数进行求和测试。测试 用例如下:
- reg add_tree_valid_0,add_tree_valid_1;
- wire add_tree_dout_valid_0,add_tree_dout_valid_1;
- reg [100*local_dw-1:0]add_tree_din_0; //输入100个数
- reg [local_dw-1:0]test_0[0:99]; //测试数据 方便查看
- wire [2*local_dw-1:0]add_tree_dout_0; //
- reg [7*local_dw-1:0]add_tree_din_1;
- reg [local_dw-1:0]test_1[0:6];
- wire [2*local_dw-1:0]add_tree_dout_1;
- integer m;
- integer n;
- //第0个通道,从1加到100
- always @(reset_l or posedge cap_clk)
- begin
- if ((~(reset_l)) == 1'b1)
- begin
- add_tree_valid_0 <= 1'b0;
- for(m=1;m<=100;m=m+1)
- begin
- add_tree_din_0[m*local_dw-1 -:local_dw]<=
- {local_dw{1'b0}};
- test_0[m-1]<= add_tree_din_0[m*local_dw-1
- -:local_dw];
- end
- endelse
- begin
- add_tree_valid_0 <= 1'b1;
- for(m=1;m<=100;m=m+1)
- begin
- add_tree_din_0[m*local_dw-1 -:local_dw]<= m;
- test_0[m-1]<= add_tree_din_0[m*local_dw-1
- -:local_dw];
- end
- end
- end
- add_tree #(local_dw,100)
- u0(
- .rst_n(reset_l),
- .clk(clk),
- .din_valid(add_tree_valid_0),
- .din(add_tree_din_0),
- .dout(add_tree_dout_0),
- .dout_valid(add_tree_dout_valid_0)
- );
- //第1个通道,生成7个10以内的随机数
- always @(reset_l or posedge cap_clk)
- begin
- if ((~(reset_l)) == 1'b1)
- begin
- add_tree_valid_1 <= 1'b0;
- for(n=1;n<=7;n=n+1)begin
- add_tree_din_1[n*local_dw-1 -:local_dw]<=
- {local_dw{1'b0}};
- test_1[n-1]<= add_tree_din_1[n*local_dw-1
- -:local_dw];
- end
- end
- else
- begin
- add_tree_valid_1 <= 1'b1;
- for(n=1;n<=7;n=n+1)
- begin
- //生成随机数
- add_tree_din_1[n*local_dw-1-:local_dw]<=
- {$random}%10;
- test_1[n-1]<= add_tree_din_1[n*local_dw-1
- -:local_dw];
- end
- end
- end
- add_tree #(local_dw,7)
- u1(
- .rst_n(reset_l),
- .clk(cap_clk),
- .din_valid(add_tree_valid_1),
- .din(add_tree_din_1),
- .dout(add_tree_dout_1),.dout_valid(add_tree_dout_valid_1)
- );
截取仿真图如图10-20所示。
u0输出5050很明显是正确的,我们来验证u1的正确性,取图10-20 所示几个典型时钟见表10-4。
明显可以验证计算正确。对于7个数目的加法运算,第一个时钟完 成3对数据的加法计算,第二个时钟完成2对数据的加法运算,第三个 时钟完成1对数据的加法运算,计算开销为3个时钟,从仿真图也可以 得到验证。 我们用quartus来查看综合后的电路。设定数据位宽为4,计算尺 寸为7。顶层电路如图10-21所示。 可见,顶层结构第一个时钟完成了三对数据的加法运算,同时与 剩余的一个数据的缓存整合为一个新的向量输入再次进行递归调用,我们继续进入这个被调用的新模块。 本模块完成了2对数据的加法,同时与剩余的单个数据缓存结果组 合为一个新的向量继续递归操作。 最后一次递归操作完成最后两个数的加法运算。至此,经过3个时 钟,完成了7个数据的加法运算。
图10-24 原图(左)与FPGA自适应分割后的结果图 3.分割验证 顶层模块的验证通过图像来进行,我们输入图10-2的不均匀光照 图像,用FPGA处理后的效果如图10-24所示。 结果与图10-3处理结果的唯一区别在于边界处理,FPGA对边界进 行了置零处理,而VC则对边界进行了置1处理。处理结果验证了我们所 设计逻辑的正确性。
我们在10.3节中详细介绍了Canny算子的原理及VC实现方法。在本 节将详细介绍如何将此算法映射到FPGA上面。 按照10.3.2节中计算Canny算子的步骤,我们列出计算流程,如图 10-25所示。
其中,如何计算高斯低通滤波和计算Sobel算子已经在线性滤波的 相关章节介绍过了。因此,设计的重点在于非最大值抑制电路和滞后 分割电路的设计。
正如我们前面所分析的,非最大值抑制主要是对Sobel运算的计算 结果进行开窗,在当前像素的3×3邻域找到梯度方向上的最大值,若 当前像素为整个方向上的最大值,则将该像素点归为潜在的边缘点。 否则,直接置为非边缘点。我们还是用数学公式来表示这一步骤,如 下所示(公式的定义请参见10.3.1节)。 我们首先需要明白当前像素的梯度值位于哪一个象限,假定其位 于第一象限,则有
设计的难点在于梯度方向上两个潜在极大值的插值运算f算子。有 两点值得我们注意: (1)f算子中包含除法运算,这是我们在FPGA中所不希望看到 的。 (2)前两个象限的除法运算的分子和分母是颠倒的,这给我们的 设计带来了难度。 由于上述两个难点,将上述算法直接映射到FPGA里面是“愚蠢” 的,我们必须对算法进行等效转换。 首先想到的是将除法转换为乘法运算。这个是很容易实现的,我 们以第一象限公式为例,两边同时乘以x,则有
接下来我们要解决x与y的差异性问题。仔细观察上述两组公式就 能发现,x与y是完全对称的,再仔细观察图10-6~图10-9,不等式右 边第一项系数为当前x与y方向梯度值的较小值,第二项系数为当前x与 y方向梯度值的较大值与最小值之差,不等式左边系数为当前x与y方向 梯度值的较大值。因此,将公式变换如下:
上式中,Mmax 代表当前x与y方向梯度值的较大值,Mmin 代表当前 x与y方向梯度值的较小值。C0 ,C1 ,C2 ,C3 则分别代表4个插值元 素。对于8个不同的象限,插值元素的索引号如表10-5所示(在实际运 算时可通过查找表实现)。
这样,我们就实现了4个主象限的计算一致性,同时将其转换为 FPGA所擅长的乘法和加法运算。 在查表得到插值元素时,需要知道当前的象限信息,得到象限信 息的最简单办法是通过查询x与y方向梯度值的符号。同时,需要得到 两个值的比较关系。读者如果认真读过前面的章节就可以知道,在介 绍Sobel运算时的Cordic运算预处理模块cordic_pre可以完美解决我们 想要的问题,我们所需要做的仅仅是例化这样一个模块而已。 需要注意的是,我们需要Sobel运算结果的x与y方向的输出,以及 模值输出,实际上并不需要方向计算。 我们给出第一阶段的计算电路如图10-26所示。
首先,将Sobel的x和y方向的计算结果通过cordic模块输出两个值 的绝对值的较大值Max和较小值Min,以及输入坐标的的象限信息 Quadrant_info。接着为了得到当前像素的8个插值元素,即当前窗 口 , 我 们 需 要 将 上 面 三 个 数 据 及 Sobel 的 模 值 结 果 Mudule 送 入 win_buf 得到 其 窗 口 缓 存 。 我们 需 要 的 是 当前 窗 口 的 9 个 元 素 Mudule(8 : 0) , 以 及 上 面 三 个 数 据 的 当 前 值 Max(4),Min(4),Quadrant_info(4)。 我们给出第二阶段的计算电路如图10-27所示。
第二阶段的计算:我们将象限信息和当前窗口像素送入查找表, 由查找表电路得到C0 ,C1 ,C2 ,C3 输出。然后,在此基础上做f算子,得到的结果与中心窗口值与Max的乘积进行比较。最后,在比较结 果的基础上进行分割。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。