FPGA学习笔记
图像处理算法
1. 中值滤波算法
1.1 原理
1.2 FPGA实现中值滤波算法
2. 应用MATLAB给图片加椒盐噪声
3. 项目结果
1. 中值滤波算法
1.1 原理
- 将每一像素点的灰度值设置为该点的某领域窗口内的所有像素点灰度值的中值。
- 特点:有效抑制噪声,保护图像边缘信息,是经典的平滑噪声方法,可用作处理RGB图像格式。
方法:
将数据按大小排序,然后根据有序的数字序列找中值,排序算法:冒泡排序,二等分排序等软件算法,适合硬件的排序算法比较少。
- 分别对三行像素进行排序
- 分别对三行像素中的3个最大,3个中间,3个最小分别进行排序
- 对最大的最小,中间的中间,最小的最大进行排序得到中值
1.2 FPGA实现中值滤波算法
-
项目目标:
将椒盐噪声图片,通过中值滤波算法,进行去噪处理,并通过FPGA显示到TFT显示屏上。
-
项目工具:
① 硬件:Intel Cyclone IV E系列FPGA开发板,5寸(800*480)TFT电容触摸显示屏;
② 软件:Quartus软件,Picture2Hex软件,MATLAB; -
项目组成模块:
① pll: 产生项目所需时钟:1. SDRAM控制器时钟;2. SDRAM时钟信号;3. TFT屏控制器时钟
② uart串口协议(uart_rx, uart_tx)
③ 读FIFO
④ 写FIFO
⑤ SDRAM控制模块
⑥ TFT屏控制模块
⑦ 中值滤波模块 -
Verilog代码
/*
1. 分别对三行像素进行排序
2. 分别对三行像素中:3个最大值,3个中间值,3个最小值,进行排序
3. 对:3最大值中的最小值,3个中间值中的中间值,3个最小值中的最大值,进行排序,得到中值
*/
module median_filter_r0(
input clk, //33MHZ
input rst_n,
input [15:0] data_in, //灰度像素输入
input data_in_en,//lcd显示有效区使能信号
input hs_in,//行同步信号输入
input vs_in,//场同步信号输入
output wire [15:0] data_out,//中值处理后灰度像素输出
output wire data_out_en,
output wire hs_out,//行同步信号输出
output wire vs_out//场同步信号输出
);
//----timing declaration----
reg hs_reg0;
reg hs_reg1;
reg hs_reg2;
reg vs_reg0;
reg vs_reg1;
reg vs_reg2;
reg de_reg0;
reg de_reg1;
reg de_reg2;
//----pipeline declaration----
wire [15:0] row0;
wire [15:0] row1;
wire [15:0] row2;
reg [15:0] r0_c0;
reg [15:0] r0_c1;
reg [15:0] r0_c2;
reg [15:0] r1_c0;
reg [15:0] r1_c1;
reg [15:0] r1_c2;
reg [15:0] r2_c0;
reg [15:0] r2_c1;
reg [15:0] r2_c2;
reg [15:0] r0_max;
reg [15:0] r0_mid;
reg [15:0] r0_min;
reg [15:0] r1_max;
reg [15:0] r1_mid;
reg [15:0] r1_min;
reg [15:0] r2_max;
reg [15:0] r2_mid;
reg [15:0] r2_min;
reg [15:0] max_max;
reg [15:0] max_mid;
reg [15:0] max_min;
reg [15:0] mid_max;
reg [15:0] mid_mid;
reg [15:0] mid_min;
reg [15:0] min_max;
reg [15:0] min_mid;
reg [15:0] min_min;
reg [15:0] mid;
//--------------------------------------------------
//----3行像素缓存--------------------------//
shifter3_3 shifter3_3(
.clken(data_in_en),
.clock(clk),
.shiftin(data_in),
.shiftout(),
.taps0x(row0),
.taps1x(row1),
.taps2x(row2)
);
//----timing control-----------------------//
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
hs_reg0 <= 1'd0;
hs_reg1 <= 1'd0;
hs_reg2 <= 1'd0;
vs_reg0 <= 1'd0;
vs_reg1 <= 1'd0;
vs_reg2 <= 1'd0;
de_reg0 <= 1'd0;
de_reg1 <= 1'd0;
de_reg2 <= 1'd0;
end
else if(data_in_en)begin
hs_reg0 <= hs_in;
hs_reg1 <= hs_reg0;
hs_reg2 <= hs_reg1;
vs_reg0 <= vs_in;
vs_reg1 <= vs_reg0;
vs_reg2 <= vs_reg1;
de_reg0 <= data_in_en;
de_reg1 <= de_reg0;
de_reg2 <= de_reg1;
end
end
//-----------------------------------------//
//----pipeline-----------------------------//
//----3*3 matix from image
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r0_c0 <= 16'd0;
r0_c1 <= 16'd0;
r0_c2 <= 16'd0;
r1_c0 <= 16'd0;
r1_c1 <= 16'd0;
r1_c2 <= 16'd0;
r2_c0 <= 16'd0;
r2_c1 <= 16'd0;
r2_c2 <= 16'd0;
end
else if(data_in_en)begin
r0_c0 <= row0;
r0_c1 <= r0_c0;
r0_c2 <= r0_c1;
r1_c0 <= row1;
r1_c1 <= r1_c0;
r1_c2 <= r1_c1;
r2_c0 <= row2;
r2_c1 <= r2_c0;
r2_c2 <= r2_c1;
end
end
//------------------------------------------------------------------
//----1.分别对三行像素进行排序r0_max, r10_max,r2_max
//----r0------------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r0_max <= 16'd0;
r0_mid <= 16'd0;
r0_min <= 16'd0;
end
else if(data_in_en)begin
if((r0_c0>=r0_c1)&&(r0_c0>=r0_c2))begin//r0_max <= r0_c0
r0_max <= r0_c0;
if(r0_c1>=r0_c2)begin
r0_mid <= r0_c1;
r0_min <= r0_c2;
end
else begin
r0_mid <= r0_c2;
r0_min <= r0_c1;
end
end
else if((r0_c1>=r0_c0)&&(r0_c1>=r0_c2))begin//r0_max <= r0_c1;
r0_max <= r0_c1;
if(r0_c0>=r0_c2)begin
r0_mid <= r0_c0;
r0_min <= r0_c2;
end
else begin
r0_mid <= r0_c2;
r0_min <= r0_c0;
end
end
else if((r0_c2>=r0_c0)&&(r0_c2>=r0_c1))begin//r0_max <= r0_c2;
r0_max <= r0_c2;
if(r0_c0>=r0_c1)begin
r0_mid <= r0_c0;
r0_min <= r0_c1;
end
else begin
r0_mid <= r0_c1;
r0_min <= r0_c0;
end
end
end
end
//----r1--------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r1_max <= 16'd0;
r1_mid <= 16'd0;
r1_min <= 16'd0;
end
else if(data_in_en)begin
if((r1_c0>=r1_c1)&&(r1_c0>=r1_c2))begin//r0_max <= r0_c0
r1_max <= r1_c0;
if(r1_c1>=r1_c2)begin
r1_mid <= r1_c1;
r1_min <= r1_c2;
end
else begin
r1_mid <= r1_c2;
r1_min <= r1_c1;
end
end
else if((r1_c1>=r1_c0)&&(r1_c1>=r1_c2))begin//r0_max <= r0_c1;
r1_max <= r1_c1;
if(r1_c0>=r1_c2)begin
r1_mid <= r1_c0;
r1_min <= r1_c2;
end
else begin
r1_mid <= r1_c2;
r1_min <= r1_c0;
end
end
else if((r1_c2>=r1_c0)&&(r1_c2>=r1_c1))begin//r0_max <= r0_c2;
r1_max <= r1_c2;
if(r1_c0>=r1_c1)begin
r1_mid <= r1_c0;
r1_min <= r1_c1;
end
else begin
r1_mid <= r1_c1;
r1_min <= r1_c0;
end
end
end
end
//----r2--------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
r2_max <= 16'd0;
r2_mid <= 16'd0;
r2_min <= 16'd0;
end
else if(data_in_en)begin
if((r2_c0>=r2_c1)&&(r2_c0>=r2_c2))begin//r0_max <= r0_c0
r2_max <= r2_c0;
if(r2_c1>=r2_c2)begin
r2_mid <= r2_c1;
r2_min <= r2_c2;
end
else begin
r2_mid <= r2_c2;
r2_min <= r2_c1;
end
end
else if((r2_c1>=r2_c0)&&(r2_c1>=r2_c2))begin//r0_max <= r0_c1;
r2_max <= r2_c1;
if(r2_c0>=r2_c2)begin
r2_mid <= r2_c0;
r2_min <= r2_c2;
end
else begin
r2_mid <= r2_c2;
r2_min <= r2_c0;
end
end
else if((r2_c2>=r2_c0)&&(r2_c2>=r2_c1))begin//r0_max <= r0_c2;
r2_max <= r2_c2;
if(r2_c0>=r2_c1)begin
r2_mid <= r2_c0;
r2_min <= r2_c1;
end
else begin
r2_mid <= r2_c1;
r2_min <= r2_c0;
end
end
end
end
//--------------------------------------------------------------------
//----2. 分别对三行像素中:3个最大值,3个中间值,3个最小值,进行排序
//----3个最大值------------------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
max_max <= 16'd0;
max_mid <= 16'd0;
max_min <= 16'd0;
end
else if(data_in_en)begin
if((r0_max >= r1_max)&&(r0_max >= r2_max))begin//max_max <= r0_max
max_max <= r0_max;
if((r1_max >= r2_max))begin
max_mid <= r1_max;
max_min <= r2_max;
end
else begin
max_mid <= r2_max;
max_min <= r1_max;
end
end
else if((r1_max >= r0_max)&&(r1_max >= r2_max))begin//max_max <= r1_max;
max_max <= r1_max;
if((r0_max >= r2_max))begin
max_mid <= r0_max;
max_min <= r2_max;
end
else begin
max_mid <= r2_max;
max_min <= r0_max;
end
end
else if((r2_max >= r0_max)&&(r2_max >= r1_max))begin//max_max <= r2_max;
max_max <= r2_max;
if((r0_max >= r1_max))begin
max_mid <= r0_max;
max_min <= r1_max;
end
else begin
max_mid <= r1_max;
max_min <= r0_max;
end
end
end
end
//----3个中间值------------------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
mid_max <= 16'd0;
mid_mid <= 16'd0;
mid_min <= 16'd0;
end
else if(data_in_en)begin
if((r0_mid >= r1_mid)&&(r0_mid >= r2_mid))begin//mid_max <= r0_mid
mid_max <= r0_mid;
if((r1_mid >= r2_mid))begin
mid_mid <= r1_mid;
mid_min <= r2_mid;
end
else begin
mid_mid <= r2_mid;
mid_min <= r1_mid;
end
end
else if((r1_mid >= r0_mid)&&(r1_mid >= r2_mid))begin//mid_max <= r1_mid;
mid_max <= r1_mid;
if((r0_mid >= r2_mid))begin
mid_mid <= r0_mid;
mid_min <= r2_mid;
end
else begin
mid_mid <= r2_mid;
mid_min <= r0_mid;
end
end
else if((r2_mid >= r0_mid)&&(r2_mid >= r1_mid))begin//mid_max <= r2_mid;
mid_max <= r2_mid;
if((r0_mid >= r1_mid))begin
mid_mid <= r0_mid;
mid_min <= r1_mid;
end
else begin
mid_mid <= r1_mid;
mid_min <= r0_mid;
end
end
end
end
//----3个最小值------------------------------------------------
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
min_max <= 16'd0;
min_mid <= 16'd0;
min_min <= 16'd0;
end
else if(data_in_en)begin
if((r0_min >= r1_min)&&(r0_min >= r2_min))begin//min_max <= r0_min
min_max <= r0_min;
if((r1_min >= r2_min))begin
min_mid <= r1_min;
min_min <= r2_min;
end
else begin
min_mid <= r2_min;
min_min <= r1_min;
end
end
else if((r1_min >= r0_min)&&(r1_min >= r2_min))begin//min_max <= r1_min;
min_max <= r1_min;
if((r0_min >= r2_min))begin
min_mid <= r0_min;
min_min <= r2_min;
end
else begin
min_mid <= r2_min;
min_min <= r0_min;
end
end
else if((r2_min >= r0_min)&&(r2_min >= r1_min))begin//min_max <= r2_min;
min_max <= r2_min;
if((r0_min >= r1_min))begin
min_mid <= r0_min;
min_min <= r1_min;
end
else begin
min_mid <= r1_min;
min_min <= r0_min;
end
end
end
end
//---------------------------------------------------------------------------------
//----3. 对:3个最大值中的最小值,3个中间值中的中间值,3个最小值中的最大值,进行排序,得到中值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
mid <= 16'd0;
else if(data_in_en)begin
if(((max_min >= mid_mid)&&(max_min < min_max))||((max_min < mid_mid)&&(max_min >= min_max)) )//mid <= max_min;
mid <= max_min;
else if(((mid_mid >= max_min)&&(mid_mid < min_max))||((mid_mid < max_min)&&(mid_mid >= min_max)) )//mid <= mid_mid;
mid <= mid_mid;
else if(((min_max >= max_min)&&(min_max < mid_mid))||((min_max < max_min)&&(min_max >= mid_mid)) )//mid <= min_max;
mid <= min_max;
else;
end
else;
end
//----result-------------------------------------------------------------------------
assign data_out = mid;
assign data_out_en = de_reg2;
assign hs_out = hs_reg2;
assign vs_out = vs_reg2;
//-----------------------------------------------------------------------------------
endmodule
2. 应用MATLAB给图片加椒盐噪声:
- MATLAB代码
close all;clear all;clc;%清除
L = imread('C:\Users\GloriaHuo\Desktop\0001.jpg'); % 在指定路径读取图像
J = imnoise(L,'salt & pepper', 0.1); %Salt&pepper 噪声密度=0.1
figure(1);
imshow(L); % 显示原图
figure(2);
imshow(J); %立即弹出窗口,显示加了加椒盐噪声后的图片
hold on;
imwrite(J,'C:\Users\GloriaHuo\Desktop\0001_saltpepper.jpg');%把加入噪声的图像保存起来
- 1. 原图像
- 2. 加入椒盐噪声图像
3. 项目结果
5寸TFT电容触摸显示屏
图像:800*480像素
(1). (易烊千玺^^)网络原图:
(2). FPGA显示原图:
(3). 加入椒盐噪声图:
-
Salt&pepper 噪声密度=0.1
(4). FPGA中值滤波处理图: -
Salt&pepper 噪声密度=0.1
项目结果分析: -
对比图3与图4可以发现,中值滤波算法可以有效地去除椒盐噪声;
参考资料:《FPGA系统设计与验证实战指南》
【注】:个人学习笔记,如有错误,望不吝赐教,这厢有礼了~~~