基于FPGA的数字图像处理- 图像分割【4.9】

10.4.5 仿真与调试

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.5 基于FPGA的Canny算子设计

        我们在10.3节中详细介绍了Canny算子的原理及VC实现方法。在本 节将详细介绍如何将此算法映射到FPGA上面。 按照10.3.2节中计算Canny算子的步骤,我们列出计算流程,如图 10-25所示。

其中,如何计算高斯低通滤波和计算Sobel算子已经在线性滤波的 相关章节介绍过了。因此,设计的重点在于非最大值抑制电路和滞后 分割电路的设计。

10.5.1 非最大值抑制电路设计

正如我们前面所分析的,非最大值抑制主要是对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的乘积进行比较。最后,在比较结 果的基础上进行分割。

  • 40
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryStarXin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值