基于FPGA的快速中值滤波算法--转载我之前的blog的内容

转:  http://xiongwei.site/          

在实时图像采集中,不可避免的会引入噪声,尤其是干扰噪声和椒盐噪声,噪声的存在严重影响边缘检测的效果,中值滤波是一种基于排序统计理论的非线性平滑计数,能有效平滑噪声,且能有效保护图像的边缘信息,所以被广泛用于数字图像处理的边缘提取,其基本原理是把数字图像或数字序列中的一点的值用该点邻域内所有点的中值来代替。

中值滤波对脉冲噪声有良好的滤除作用,特别是在滤除噪声的同时,能够保护信号的边缘,使之不被模糊。这些优良特性是线性滤波方法所不具有的。此外,中值滤波的算法比较简单,也易于用硬件实现。所以,中值滤波方法一经提出后,便在数字信号处理领得到重要的应用。

中值滤波方法是,对待处理的当前像素,选择一个模板,该模板为其邻近的若干个像素组成,对模板的像素由小到大进行排序,再用模板的中值来替代原像素的值的方法。

当我们使用3x3窗口后获取领域中的9个像素,就需要对9个像素值进行排序,为了提高排序效率,排序算法思想如图3-18所示

(1) 对窗内的每行像素按降序排序,得到最大值、中间值和最小值;

(2) 把三行的最小值相比较,取其中的最大值;

(3) 把三行的最大值相比较,取其中的最小值;

(4) 把三行的中间值相比较,再取一次中间值;

(5) 把前面的到的三个值再做一次排序,获得的中值即该窗口的中值。

中值滤波的3x3矩阵的生成和均值滤波是完全类似的。我们求中值的方法是,先对3x3矩阵的每行按从大到小进行排序,然后利用排序法求出最大值那一列的最小值,求出之间数那一列的中间值,求出最小值按一列的最大值,最后将求出的三个值再排序,这三个值的中间值就是这个3x3矩阵的中间值。

这里我们开始想一个问题,用3x3矩阵排序来替代中间值,那么图像的边缘区域怎么办?这是一个问题,在这里我的做法是对齐置之不理。怎么生成3x3的移动窗口,而且图像的像素有比较大,那么我们需要对其进行行缓存。一般做法是ram和fifo,还有就是altera公司的shift_ram(貌似专门为图像处理而生),因为我是基于Xilinx平台的ise,所以这里我自己设计了一个类似shift_ram的ip,基于两个fifo,深度为512. 下面就是行缓存的代码:

module shift_line_buffer(
				input 	wire	 		line_clk			,
				input 	wire			s_rst_n				,	
				input	wire 			in_line_vaild		,//在有效数据提前一个周期
				input 	wire 	[7:0]	din					,
				input  	wire			vsync				,//帧同步
				output 	wire	[7:0] 	taps0x				,
				output 	wire	[7:0] 	taps1x				,
				output	wire	[7:0] 	taps2x				,
				output  wire			done
);

`ifndef SIM
localparam IMAGE_WIDTH = 480 ;//
`else
localparam IMAGE_WIDTH = 8 ;//480X272
`endif

wire empty1,empty2,pop1_en,pop2_en;
reg rd1_en,rd2_en;

wire prog_full1,prog_full2;
assign pop1_en=(prog_full1==1)?1'b1:vsync;//0?
always @(posedge line_clk or negedge s_rst_n)begin
	if(s_rst_n == 0)
		rd1_en=0;
	else if(pop1_en== 1 && in_line_vaild== 1)
		rd1_en=1;
	else if(empty1==1)
		rd1_en=0;		
end

fifo_512x8	fifo_512x8_inst1(
  .clk						(line_clk),
  .rst						(!s_rst_n),
  .din						(din),
  .wr_en					(in_line_vaild),
  .rd_en					(rd1_en),
  .prog_full_thresh			(IMAGE_WIDTH-3),//PROG_FULL_THRESH:用来设定PROG_FULL的有效时的数据数目,和无效时的数据数目
  .dout						(taps1x),
  .full						(),
  .empty					(empty1),
  .prog_full                (prog_full1)
);

assign pop2_en=(prog_full2==1)?1'b1:vsync;
always @(posedge line_clk or negedge s_rst_n)begin
	if(s_rst_n == 0)
		rd2_en=0;
	else if(pop2_en== 1 && in_line_vaild== 1)
		rd2_en=1;
	else if(empty2==1)
		rd2_en=0;		
end

assign out_line_vaild= rd1_en;
fifo_512x8	fifo_512x8_inst2(
  .clk						(line_clk),
  .rst						(!s_rst_n),
  .din						(taps1x),
  .wr_en					(out_line_vaild),
  .rd_en					(rd2_en),
  .prog_full_thresh			(IMAGE_WIDTH-3),//PROG_FULL_THRESH:用来设定PROG_FULL的有效时的数据数目,和无效时的数据数目
  .dout						(taps0x),
  .full						(),
  .empty					(empty2),
  .prog_full                (prog_full2)
);

assign taps2x=din;

reg done_r;
//assign done= rd2_en;
always @(posedge line_clk )begin
	//done<=rd2_en;
	done_r<=rd1_en;
end

assign done= done_r;

endmodule

根据上述图中描述的快速中值算法,代码:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    10:29:01 06/12/2015 
// Design Name: 
// Module Name:    median 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module big(input  clk,rst_n,din_flag,
              input  wire[7:0] p1,p2,p3,p4,p5,p6,p7,p8,p9,
				  output reg [7:0] median
              );
wire [7:0] max0,med0,min0,	max1,med1,min1,max2,med2,min2,
          
			   max_min,	med_med,
            min_max,final_med;	
(*KEEP="TRUE"*)	 wire [7:0]	 max_max, max_med,med_max,med_min,min_med,min_min,final_max,final_min;	  
middle m0 (
    .clk(clk), 
    .a(p1), 
    .b(p2), 
    .c(p3), 
    .max(max0), 
    .med(med0), 
    .min(min0)
    );
middle m1 (
    .clk(clk), 
    .a(p4), 
    .b(p5), 
    .c(p6), 
    .max(max1), 
    .med(med1), 
    .min(min1)
    );
middle m2 (
    .clk(clk), 
    .a(p7), 
    .b(p8), 
    .c(p9), 
    .max(max2), 
    .med(med2), 
    .min(min2)
    );	 
middle m3 (
    .clk(clk), 
    .a(max0), 
    .b(max1), 
    .c(max2), 
    .max(max_max), 
    .med(max_med), 
    .min(max_min)
    );

middle m4 (
    .clk(clk), 
    .a(med0), 
    .b(med1), 
    .c(med2), 
    .max(med_max), 
    .med(med_med), 
    .min(med_min)
    );
middle m5 (
    .clk(clk), 
    .a(min0), 
    .b(min1), 
    .c(min2), 
    .max(min_max), 
    .med(min_med), 
    .min(min_min)
    );
middle m6 (
    .clk(clk), 
    .a(min_max), 
    .b(med_med), 
    .c(max_min), 
    .max(final_max), 
    .med(final_med), 
    .min(final_min)
    );
	 
	 
	 always @(posedge clk)
	    if(!rst_n)
		 median<=0;
		 else
	      begin
			  if(din_flag)
			   begin
			   
				 //median<=max_med;
				 median<=final_med;
				end

			end	

			
endmodule

对该模块进行仿真,发现数据都是滞后4个clk,所以代码的数据有效信号也进行了打拍处理!

滤波窗口:

module fliter_window(
			input	wire	[7:0]	din,
			input 	wire 			data_valuable,
			input 					sclk,
			input					s_rst_n,
			input					vsync,
			
			output 	wire 	[7:0]	Data02,
			output 	wire 	[7:0]	Data12,
			output 	wire 	[7:0]	Data22,
			
			output 	reg 	[7:0]	Data01,	
			output 	reg 	[7:0]	Data00,
			output 	reg 	[7:0]	Data11,
			output 	reg 	[7:0]	Data10,
			output 	reg 	[7:0]	Data21,	
			output 	reg 	[7:0]	Data20,					
			output	wire 			dout_flag
			
);

//reg [7:0]Data00,Data10,Data20,Data01,Data02,Data11,Data12,Data21,Data22;//3x3矩阵
wire done;
reg[4:0] dout_flag_r;//根据仿真需要打6拍
always@ (posedge sclk )begin
dout_flag_r<={dout_flag_r[3:0],done};
end
/* reg [3:0] data_valuable_r;
always@ (posedge sclk )begin
data_valuable_r<={data_valuable_r[2:0],data_valuable};
end */

//assign dout_flag=dout_flag_r[4]&data_valuable_r[3];

assign dout_flag=dout_flag_r[4];

shift_line_buffer	shift_line_buffer_inst(
					.line_clk				(sclk			),
					.s_rst_n			(s_rst_n		)	,	
					.in_line_vaild		(data_valuable),//
					.din				(din			)	,
					.vsync				(vsync),
					.taps0x				(Data02			),
					.taps1x				(Data12			),
					.taps2x				(Data22		),
					.done              ( done         )
);	



//get data in the window
	always@(posedge sclk or negedge s_rst_n)
	begin
		if (!s_rst_n)
			begin
				Data01<=0;
				Data00<=0;
				Data11<=0;
				Data10<=0;
				Data21<=0;
				Data20<=0;						
			end
		else
			begin
				{Data00,Data01}<={Data01,Data02};
				{Data10,Data11}<={Data11,Data12};
				{Data20,Data21}<={Data21,Data22};			
			end
	end		
endmodule

top模块:

module Fast_median_filter(
				input	wire	[7:0]	din,
				input 	wire 			data_valuable,
				input 					sclk,
				input					s_rst_n,
				input					vsync,
				
				output wire [7:0] median,
				output	wire 			dout_flag
);

wire[7:0]Data00,Data01,Data02,Data10,Data11,Data12,Data20,Data21,Data22;
fliter_window		fliter_window_inst(
				.din			(din),
				.data_valuable	(data_valuable),
				.sclk			(sclk),
				.s_rst_n		(s_rst_n),
				.vsync			(vsync),
				
				.Data02			(Data02		),	
				.Data12			(Data12		),
				.Data22			(Data22		),
				                 
				.Data01			(Data01		),	
				.Data00			(Data00		),
				.Data11			(Data11		),
				.Data10			(Data10		),
				.Data21			(Data21		),	
				.Data20			(Data20		),					
				.dout_flag     ( dout_flag )
				
);

big			big_inst( 
			.clk				(sclk),
			.rst_n				(s_rst_n),
			.din_flag		(dout_flag),
			.p1				(Data00),
			.p2				(Data01),
			.p3				(Data02),
			.p4				(Data10),
			.p5				(Data11),
			.p6				(Data12),
			.p7				(Data20),
			.p8				(Data21),
			.p9				(Data22),
			.median         ( median)
          );
		  

endmodule

测试脚本:

`timescale 1ns/1ns

module tb_Fast_median_filter;

reg sclk;
reg s_rst_n;
reg in_line_vaild;

reg [7:0] din;

wire		dout_flag;
wire[7:0]	median;
reg [7:0] mem_a[130559:0];//480*272=130560-----13bit
//------------- generate system signals ------------------------------------
initial begin
	sclk = 1;
	s_rst_n <= 0;
	din<='d0;
in_line_vaild<=0;
#100
	s_rst_n <= 1;
	#100

	data_generate();
end

always #10 sclk = ~sclk;

initial $readmemh("./1.txt", mem_a);
//initial $readmemh("./test.txt", mem_a);
//initial $readmemh("./1.txt", mem_a);
//modelsim仿真wave中数据变量导出到txt文档

reg [17:0] i =0;  
always @ (posedge sclk)  
begin  
    if (!s_rst_n)  
        i <=0;  
   else if (i<130559 && dout_flag==1) 
   //else if (i<47 && dout_flag==1) 
        i <= i+1;  
    else  
        i<= 0;  
end  
      
  
integer w_file;  
    initial w_file = $fopen("data_out_1.txt");  
    always @(i)  
    begin  
		
        $fdisplay(w_file,"%d",median); 
	if (i == 17'd130559)    //  
	//if (i == 17'd47) 
            $stop; 
 
 
    end 
	
//modelsim仿真wave中数据变量导出到txt文档

Fast_median_filter	Fast_median_filter_inst(
				.din			(din			),
				.data_valuable	(in_line_vaild),
				.sclk			(sclk			),
				.s_rst_n		(s_rst_n		),
				.vsync			(vsync			),
				
				.median			(median			),
				.dout_flag      (dout_flag    )
);

task data_generate();
integer i;
begin
for(i=0; i<130560; i=i+1) begin
//for(i=0; i<50; i=i+1) begin
	 #20 
 in_line_vaild <= 1'b1;
 din<=mem_a[i];
 //#20 din<=din+1;
 end
 in_line_vaild<=0;
end
endtask

endmodule

整个模块的modelsim的仿真图:

该模块的RTL图:

MATLAB处理FPGA得到的数据:

clc;
clear all;
close all;
medfilt_v_load = load('.\data_out_1.txt'); % verilog 产生的中值滤波之后数据
m = 480;
n = 272;
medfilt_v = reshape(medfilt_v_load, m, n);
medfilt_v = uint8(medfilt_v');
imshow(medfilt_v)

用MATLAB中值滤波处理的结果:

clc;
clear;
i=imread('1.jpg');
A=rgb2gray(i);
subplot(1,2,1);
imshow(A);
title('原始图像');

[r,c]=size(A);
 fid=fopen('1.txt','wt');  
 for i=1:r  
     for j=1:c  
         fprintf(fid,'%d\t ',A(i,j));  
     end  
             fprintf(fid,'\n');  
 end  
 fclose(fid);
 
i=imread('1.jpg');
A=rgb2gray(i);
k=medfilt2(A);
subplot(1,2,2);
imshow(k);
title('处理后图像');
[r,c]=size(k);
 fid=fopen('2.txt','wt');  
 for i=1:r  
     for j=1:c  
         fprintf(fid,'%d\t ',k(i,j));  
     end  
             fprintf(fid,'\n');  
 end  
fclose(fid); 

最后看一下效果图: 预处理的图片:

用FPGA处理的结果:

用MATLAB处理的结果:

从图中看出效果都几乎差不多,但是用FPGA处理的时间大大减小,这也为后面的FPGA特征提取奠定了基础。用FPGA来实现实时图像处理,提高机器人的定位精度。 如果想要整个工程的源代码,可以叫本人qq:1010944344索要。

猜你喜欢

转载自blog.csdn.net/Laplace666/article/details/83826443