1. 程式人生 > >基於FPGA的快速中值濾波演算法--轉載我之前的blog的內容

基於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拍
[email protected] (posedge sclk )begin
dout_flag_r<={dout_flag_r[3:0],done};
end
/* reg [3:0] data_valuable_r;
[email protected] (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
	[email protected](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索要。