数字逻辑课程的自由设计中,我编写了一个3x3开窗的中值滤波器,处理一副128*128像素值的图像,并且最终可以在FPGA上板实现。

中值滤波的本质就是对于一个n*n的窗口,将其内部的值进行排序,取中位数作为中间的点的值。通过中值滤波可以很好的减弱图片的噪声,并且报纸边缘不变。

中值滤波的硬件实现主要分为三个模块:开窗模块、中值计算模块与存储器模块。

【1】开窗模块主要需要利用循环同步计数器来实现,分别进行列和位置的循环,从而达到最终开窗的目的。

开窗模块的代码如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////
// Q为0~127计数的循环计数器,pos为1~128的循环计数器 //
// clk为输入时钟, rst_n为低电平有效的reset信号,en为高电平有效的使能端 //
//////////////////////////////////////////////////////////////////////////////
module Counter14(clk, rst_n, en, mm, aa, bb, cc, dd, ee, ff, gg, hh, jj); input clk, rst_n, en; output [:]mm,aa,bb,cc,dd,ee,ff,gg,hh,jj;
wire [:]m_,Q_,a2,b2,c2,d2,e2,f2,g2,h2,j2;
wire [:]m,Q,pos,pos2,a,b,c,d,e,f,g,h,j;
wire [:]i; wire posm_w; not not1(posm_w, pos2[]);
assign Q_ = Q[:]; // 13位0~127计数器 // assign shamt2 = shamt[13:0];// 13位移位数据
assign mm = m[:];
assign a2 = a[:];
assign b2 = b[:];
assign c2 = c[:];
assign d2 = d[:];
assign e2 = e[:];
assign f2 = f[:];
assign g2 = g[:];
assign h2 = h[:];
assign j2 = j[:]; wire Q0, Q1, Q2, Q3, Q4, Q5;
wire m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12;
wire [:]a_,b_,c_,d_,e_,f_,g_,h_,j_;
wire [:]ya,yb,yc,yd,ye,yf,yg,yh,yj; T_trigger t1(.clk(clk), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN(Q0));
T_trigger t2(.clk(Q0), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN(Q1));
T_trigger t3(.clk(Q1), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN(Q2));
T_trigger t4(.clk(Q2), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN(Q3));
T_trigger t5(.clk(Q3), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN(Q4));
T_trigger t6(.clk(Q4), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN(Q5));
T_trigger t7(.clk(Q5), .rst_n(rst_n), .EN(en), .Q(Q[]), .QN()); T_trigger mm1(.clk(clk), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m0));
T_trigger mm2(.clk(m0), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m1));
T_trigger mm3(.clk(m1), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m2));
T_trigger mm4(.clk(m2), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m3));
T_trigger mm5(.clk(m3), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m4));
T_trigger mm6(.clk(m4), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m5));
T_trigger mm7(.clk(m5), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m6));
T_trigger mm8(.clk(m6), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m7));
T_trigger mm9(.clk(m7), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m8));
T_trigger mm10(.clk(m8), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m9));
T_trigger mm11(.clk(m9), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m10));
T_trigger mm12(.clk(m10), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m11));
T_trigger mm13(.clk(m11), .rst_n(rst_n), .EN(en), .Q(m[]), .QN(m12));
T_trigger mm14(.clk(m12), .rst_n(rst_n), .EN(en), .Q(m[]), .QN()); // 将Q拓展为16位
assign Q[:] = 'b000000000;
// 将m拓展为16位
assign m[:] = 'b00;
// 得到行数
assign i[:] = m[:];
// 由行数得到偏移量
// assign shamt[15:0] = {2'b00,i[6:0],7'b0000000}; // 构造1~128循环的位置信号
add_16_prefix add1(.A(Q), .B('b0000000000000001), .Cin_s(1'b0), .S(pos), .Cout()); // 计算该像素点序号1~128*128
// add_16_prefix add2(.A(m), .B(16'b0000000000000001), .Cin_s(1'b0), .S(m_1), .Cout()); // 计算输出地址
// add_16_prefix add3(.A(m_1), .B(16'b0000000010000001), .Cin_s(1'b0), .S(output_des), .Cout()); add_16_prefix add5(.A(pos), .B('b0000000000000001), .Cin_s(1'b0), .S(pos2), .Cout()); // 计算开窗地址
assign a_ = m;
add_16_prefix add02(.A(a_), .B('b0000000000000001), .Cin_s(1'b0), .S(b_), .Cout());
add_16_prefix add03(.A(b_), .B('b0000000000000001), .Cin_s(1'b0), .S(c_), .Cout());
add_16_prefix add04(.A(a_), .B('b0000000010000000), .Cin_s(1'b0), .S(d_), .Cout());
add_16_prefix add05(.A(d_), .B('b0000000000000001), .Cin_s(1'b0), .S(e_), .Cout());
add_16_prefix add06(.A(e_), .B('b0000000000000001), .Cin_s(1'b0), .S(f_), .Cout());
add_16_prefix add07(.A(d_), .B('b0000000010000000), .Cin_s(1'b0), .S(g_), .Cout());
add_16_prefix add08(.A(g_), .B('b0000000000000001), .Cin_s(1'b0), .S(h_), .Cout());
add_16_prefix add09(.A(h_), .B('b0000000000000001), .Cin_s(1'b0), .S(j_), .Cout()); // 超出地址都指向0
genvar u;
generate
for (u = ; u < ; u = u + )
begin : layer0
and ora(a[u], ~a_[], a_[u]);
and orb(b[u], ~b_[], b_[u]);
and orc(c[u], ~c_[], c_[u]);
and ord(d[u], ~d_[], d_[u]);
and ore(e[u], ~e_[], e_[u]);
and orf(f[u], ~f_[], f_[u]);
and org(g[u], ~g_[], g_[u]);
and orh(h[u], ~h_[], h_[u]);
and orj(j[u], ~j_[], j_[u]);
end
endgenerate genvar u2;
generate
for (u2 = ; u2 < ; u2 = u2 + )
begin : layer1
and aa1(ya[u2], posm_w, a2[u2]);
and aa2(yb[u2], posm_w, b2[u2]);
and aa3(yc[u2], posm_w, c2[u2]);
and aa4(yd[u2], posm_w, d2[u2]);
and aa5(ye[u2], posm_w, e2[u2]);
and aa6(yf[u2], posm_w, f2[u2]);
and aa7(yg[u2], posm_w, g2[u2]);
and aa8(yh[u2], posm_w, h2[u2]);
and aa9(yj[u2], posm_w, j2[u2]);
end
endgenerate genvar u3;
generate
for (u3 = ; u3 < ; u3 = u3 + )
begin : layer2
and an1(aa[u3], rst_n, ya[u3]);
and an2(bb[u3], rst_n, yb[u3]);
and an3(cc[u3], rst_n, yc[u3]);
and an4(dd[u3], rst_n, yd[u3]);
and an5(ee[u3], rst_n, ye[u3]);
and an6(ff[u3], rst_n, yf[u3]);
and an7(gg[u3], rst_n, yg[u3]);
and an8(hh[u3], rst_n, yh[u3]);
and an9(jj[u3], rst_n, yj[u3]);
end
endgenerate endmodule

其中为了保持整体的一致性,我们所有模块的代码都尽量使用门级的、结构化语言来描写。在开窗模块中,还用到了T触发器这一子模块,其代码如下所示:

`timescale 1ns / 1ps

module T_trigger(clk, rst_n, EN, Q, QN);

    input clk, rst_n, EN;
output reg Q;
output QN; assign QN = ~Q; always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
Q <= ;
else if(EN)
Q <= ~Q;
else;
end endmodule

【2】取中值模块我们采用三分法的原理,对于每一行进行比较,然后再通过一轮比较得到最终结果。对于9个数字取中位数一共只用到了21个两两比较器。具体的算法如下图中所示:

比较器模块的代码如下:

`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////// //
////////////////////////////////////////////////////////////////////////////////// module median_comparator(
A,B,C,D,E,F,G,H,I,
median
);
input [:]A,B,C,D,E,F,G,H,I;
output [:]median;
wire [:]max1,max2,max3,med1,med2,med3,min1,min2,min3;
wire [:]max_2,med_2,min_2;
//第一层
comparator_3_3 mycomparator_3_3_1(A,B,C,max1,med1,min1);
comparator_3_3 mycomparator_3_3_2(D,E,F,max2,med2,min2);
comparator_3_3 mycomparator_3_3_3(G,H,I,max3,med3,min3); //第二层
comparator_3_3 mycomparator_3_3_max(.A(max1),.B(max2),.C(max3),.max(),.med(),.min(max_2));
comparator_3_3 mycomparator_3_3_med(.A(med1),.B(med2),.C(med3),.max(),.med(med_2),.min());
comparator_3_3 mycomparator_3_3_min(.A(min1),.B(min2),.C(min3),.max(min_2),.med(),.min()); //第三层
comparator_3_3 mycomparator_3_3_median(.A(max_2),.B(med_2),.C(min_2),.max(),.med(median),.min());
endmodule

【3】最后是一个自己写的9输入9输出的RAM,可以达到在同一个时钟上升沿来到时,同步提取所有开窗地址对应的数据:

`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////// module ram(
CLK,rst_n,a, b, c, d, e, f, g, h, j,
A,B,C,D,E,F,G,H,J
);
input CLK,rst_n;
input [:]a, b, c, d, e, f, g, h, j;//输入地址
output [:]A,B,C,D,E,F,G,H,J;//输出数据 reg [:]RAM[:];//深度65536,位宽8 always @(posedge CLK or negedge rst_n)
begin
if (!rst_n)
begin
A <= 'b0;
B <= 'b0;
C <= 'b0;
D <= 'b0;
E <= 'b0;
F <= 'b0;
G <= 'b0;
H <= 'b0;
J <= 'b0;
end
else
A <= RAM[a];
B <= RAM[b];
C <= RAM[c];
D <= RAM[d];
E <= RAM[e];
F <= RAM[f];
G <= RAM[g];
H <= RAM[h];
J <= RAM[j];
end
endmodule

最终我们在Matlab中手动对一幅图像添加噪声,然后再分别比较Matlab自带的中值滤波器滤波后的结果,与我们的硬件实现的中值滤波器的结果。结果比较如下:

可以看出,硬件实现的中值滤波器与Matlab自带的中值滤波效果无差别,最终两者的像素值平均值之差为0.034,误差小于0.1%,因此可以视为高度有效的硬件实现。

3x3开窗中值滤波器的FPGA硬件实现的更多相关文章

  1. 图像处理:卷积模块FPGA 硬件加速

    本文记录了利用FPGA加速图像处理中的卷积计算的设计与实现.实现环境为Altera公司的Cyclone IV型芯片,NIOS II软核+FPGA架构. 由于这是第一次设计硬件加速模块,设计中的瑕疵以及 ...

  2. 干货分享,FPGA硬件系统的设计技巧

    PGA的硬件设计不同于DSP和ARM系统,比较灵活和自由.只要设计好专用管脚的电路,通用I/O的连接可以自己定义.因此,FPGA的电路设计中会有一些特殊的技巧可以参考. 1. FPGA管脚兼容性设计 ...

  3. Verilog代码和FPGA硬件的映射关系(四)

    其实在FPGA的开发中理想情况下FPGA之间的数据要通过寄存器输入.输出,这样才能使得延时最小,从而更容易满足建立时间要求.我们在FPGA内部硬件结构中得知,IOB内是有寄存器的,且IOB内的寄存器比 ...

  4. Verilog代码和FPGA硬件的映射关系(一)

    代码和硬件之间的映射关系是一个很奇妙的过程,也展现出人类的智慧.单片机内部的硬件结构都是固定的,无法改变,我们通过代码操作着寄存器的读写,来执行各种复杂的任务.FPGA的硬件结构并不像单片机一样是固定 ...

  5. Verilog代码和FPGA硬件的映射关系(三)

    组合逻辑和FPGA之间的映射关系我们知道了,那时序逻辑和FPGA之间又是一种怎样的映射关系呢?我们就以前面寄存器章节的例子来向大家说明,也一同把当时为什么用异步复位更节约资源的原因告诉大家.我们先来看 ...

  6. Verilog代码和FPGA硬件的映射关系(二)

    大家可能会有这样的疑问,我们编写的Verilog代码最终会在FPGA上以怎样的映射关系来实现功能呢?我们以一个最简单的组合逻辑与门为例来向大家说明.RTL代码如下所示: //------------- ...

  7. FPGA硬件加速

    FPGA市场占有率最高的两大公司Xilinx和Altera. 查找表(Look-Up-Table)简称为LUT,LUT本质上就是一个RAM.目前FPGA中多使用4输入的LUT,所以每一个LUT可以看成 ...

  8. Verilog代码和FPGA硬件的映射关系(五)

    既然我们可以指定寄存器放在IOB内,那我们同样也可以指定PLL的位置.首先要确保我们有多个PLL才行.如图1所示,我们所使用的EP4CE10F17C8芯片刚好有两个. 图 1 为了演示这个例子,我们使 ...

  9. eFPGA与FPGA SoC,谁将引领下一代可编程硬件之潮流?|半导体行业观察

    eFPGA:冉冉升起的新星 eFPGA即嵌入式FPGA(embedded FPGA),是近期兴起的新型电路IP. 随着摩尔定律越来越接近瓶颈,制造ASIC芯片的成本越来越高.因此,设计者会希望ASIC ...

随机推荐

  1. HTML学习---基础知识学习

    1.1. HTML 1.为什么要有HTML? "Hello" "<h1>Hello</h1>" - 浏览器渲染时使用一套HTML规则, ...

  2. OSG3.0.1的编译

    在OSG-中国有很多关于OSG的资料,包括OSG的编译和教程. 要编译OSG首先需要准备的包: 1,OSG3.0.1源代码: 2,CMAKE: 3,OSG用到的第三方库: 4,OSG Data:OSG ...

  3. 沉淀,再出发:XPath的理解和使用

    沉淀,再出发:XPath的理解和使用 一.前言   在很多查找的场合之下,我们需要使用正则表达式和其他的查找工具来进行内容的匹配和查找,特别是对于xml文件,我们可以使用xpath等工具来进行查找,通 ...

  4. Python2.7 - IMOOC - 1

    第一章 课程介绍 python 特点:优雅.明确.简单 python适合的领域:1.Web网站和各种网络服务:2.系统工具和脚本:3.作为"胶水"语言把其他语言开发的模块包装起来方 ...

  5. 做 fzu oj 1003 简单的枚举

    暴力求解法---简单枚举 定义一个函数(函数的内容大概是包含了题目所给的限制条件),然后主函数就是通过循环进行枚举,枚举出可能的元素,带入函数中进行验证,如果符合函数所给的情况,则为其解.

  6. python中的装饰

    在面向对象(OOP)的设计模式中,decorator被称为装饰模式.OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator ...

  7. 从零搭建vue

    第一步: 安装node.js,一般安装 长期维护版  相对比较稳定 点击下载,下载好了之后双击运行,可选择安装路径,然后一路下一步即可. 安装完成后,在cmd输入 node -v 如果出现版本号,则安 ...

  8. 数据包式套接字:基于UDP协议的Socket网络编程

    步骤: 1.利用DatagramPacket封装数据包: 2.利用DatagramSocket发送数据包: 3.利用DatagramSocket接收数据包: 4.利用DatagramPacket处理数 ...

  9. Xcode 控制台打印Unicode字符串转换为中文

    在Xcode的控制台里直接打印一个数组或者字典,输出的都是一些Unicode的编码,不方便调试.    要想看到中文,则要去获取对应的key或者数组下标.得到具体某一个对象才能看到中文,给我们调试起来 ...

  10. Linq 和 SQL的左连接、右连接、内链接

    在我们工作中表连接是很常用的,但常用的有这三种连接方式:左连接.右连接.内链接 在本章节中讲的是1.如何在Linq中使用左连接,右连接,内连接. 2.三种连接之间的特点在哪? 3.Linq的三种连接语 ...