verilog實現中值濾波
轉自:https://www.cnblogs.com/happyamyhope/
前言:
首先謝謝原博主的文章,對我的幫助很大,提供了一個完整的思路,極大方便了我將演算法移植到FPGA上。
實現步驟:
1.查看了中值濾波實現相關的網站和paper;
2.按照某篇paper的設計思想進行程式設計實現;
3.對各個模組進行語法檢查、波形模擬、時序設計、除錯驗證;
4.與matlab的中值濾波結果進行比較。
實現過程:
1.查看了中值濾波實現相關的網站和paper;
在網上看了很多中值濾波的設計,也有一些程式碼可以下載,也有一片講解的,只是感覺講解的比較模糊而且不完整,最後看了幾篇碩士論文,論文竟然主要做了中值濾波的工作,發現了一些設計思路,然後就按照自己的想法進行設計。
2.按照某篇paper的設計思想進行程式設計實現;
整個中值濾波模組分為幾個小的模組:3*3視窗生成模組、計數器控制模組、3*3中值濾波模組、頂層模組以及最後的測試模組testbench的編寫。
整個框架的設計如下圖所示(使用visio畫的框架圖):
各個模組的設計:
1)ROM IP核的生成,用於儲存原始灰度影象的資料。
可以參考http://www.cnblogs.com/happyamyhope/p/5498745.html
使用matlab生成.coe影象資料檔案,然後使用Xilinx ISE工具將.coe檔案新增到ROM核進行資料初始化,按步驟得到ROM模組,參考生成的.v檔案在頂層模組直接呼叫即可。
rom_512by512 rom_512by512_inst
(
.clka(CLK), //input clka;
.addra(rom_addr), //input-from
.douta(rom_data) //output-to
);
注意ROM的儲存空間的大小;
2)3*3視窗生成模組,用於生成濾波的滑動視窗,得到視窗內的所有元素資料。
功能:
(1)根據中心畫素點得到所在其所在的行、列位置;
(2)根據該模組的開始訊號設計得到獲取資料的有效時間序列;
(3)在讀取資料的有效時序內,得到視窗內的所有元素資料;
(4)視窗資料的獲取按照一定的時序順序來獲得,類似於黑金推薦的“仿順序操作”,這個比較適合my style;不過後來發現除錯的過程中被專案組的硬體人員改動了一些,甚至說不好,感覺可能是本人還沒有理解掌握吃透“仿順序操作”的精髓吧。
(5)根據中心畫素點的行、列位置資訊得到每個視窗元素的ROM地址,根據某一時刻ROM地址,下一時刻呼叫ROM模組得到對應的元素資料,下一時刻將資料鎖存,然後再讀取該地址的資料;所以要注意地址和資料的獲取不是在同一時刻,而是需要延遲兩個時刻;
(6)還需要注意的是影象的邊界問題的特殊化處理;一般影象處理都會遇到邊界問題,這個需要謹慎;
(7)對matlab的中值濾波函式medfilt2原理的深入掌握對我們編寫這一模組非常重要。matlab並沒有主要過程的程式碼,看註釋預設情況下邊界元素設定為0,這也可以通過結果反推回去發現的。
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09:27:48 05/18/2016
// Design Name:
// Module Name: win3by3_gen
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module win3by3_gen(
CLK,
RSTn,
center_pix_sig,
cols, // the column numbers of the input image
rows,
rom_data_win, //input-from U1;
column_addr_sig, //input-from U3; //output [9 : 0] addra;
row_addr_sig, //input-from U3; //output [9 : 0] addra;
rom_addr_sig, //output-to U1;
data_out0, //output-to U4;
data_out1,
data_out2,
data_out3,
data_out4,
data_out5,
data_out6,
data_out7,
data_out8,
win_data_done_sig //output-to U4/U3;complete the win data;
);
input CLK;
input RSTn;
input [7:0] rom_data_win;
input [9:0] cols;
input [9:0] rows;
input center_pix_sig; //
input [9:0] column_addr_sig;
input [9:0] row_addr_sig;
output [7:0] data_out0; //output-to U4;
output [7:0] data_out1;
output [7:0] data_out2;
output [7:0] data_out3;
output [7:0] data_out4;
output [7:0] data_out5;
output [7:0] data_out6;
output [7:0] data_out7;
output [7:0] data_out8;
output [17:0] rom_addr_sig;
output win_data_done_sig;
/***********************************************************************************/
reg [9:0] m;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
m <= 10'd1;
else if ( center_pix_sig )
m <= row_addr_sig[9:0];
/***********************************************************************************/
reg [9:0] n;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
n <= 10'd1;
else if ( center_pix_sig )
n <= column_addr_sig[9:0];
/***********************************************************************************/
reg [3:0] i;
reg isWinDone;
reg [17:0] rom_addr;
reg [7:0] a11;
reg [7:0] a12;
reg [7:0] a13;
reg [7:0] a21;
reg [7:0] a22;
reg [7:0] a23;
reg [7:0] a31;
reg [7:0] a32;
reg [7:0] a33;
/***********************************************************************************/
reg get_9point_vld;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
get_9point_vld <= 1'b0;
else if ( center_pix_sig )
get_9point_vld <= 1'b1;
else if ( i==4'd10 )
get_9point_vld <= 1'b0;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
isWinDone <= 1'b0;
else if ( i==4'd10 )
isWinDone <= 1'b1;
else
isWinDone <= 1'b0;
always @ ( posedge CLK or negedge RSTn )
if ( !RSTn )
i <= 4'd0;
else if (i == 4'd10)
i <= 4'd0;
else if ( get_9point_vld )
i <= i + 1'b1;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
rom_addr <= 0;
else if ( get_9point_vld)
case (i)
4'd0:
if(!(m==1 || n==1)) rom_addr <= (m-2)*cols + (n-1) -1;
4'd1:
if(!(m==1 )) rom_addr <= (m-2)*cols + n -1;
4'd2:
if(!(m==1 || n==cols)) rom_addr <= (m-2)*cols + (n+1) -1;
4'd3:
if(!(n==1)) rom_addr <= (m-1)*cols + (n-1) -1;
4'd4:
rom_addr <= (m-1)*cols + n -1;
4'd5:
if(!(n==cols)) rom_addr <= (m-1)*cols + (n+1) -1;
4'd6:
if(!(m==cols || n==1)) rom_addr <= m*cols + (n-1) -1;
4'd7:
if(!(m==cols)) rom_addr <= m*cols + n -1;
4'd8:
if(!(m==cols || n==cols)) rom_addr <= m*cols + (n+1) -1;
default:;
endcase
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
a11 <= 0;
a12 <= 0;
a13 <= 0;
a21 <= 0;
a22 <= 0;
a23 <= 0;
a31 <= 0;
a32 <= 0;
a33 <= 0;
end
else if ( get_9point_vld )
case (i)
4'd2:
if ( m==1 || n==1 )
a11 <= 0;
else
a11 <= rom_data_win;
4'd3:
if ( m==1 ) a12 <= 0;
else a12 <= rom_data_win;
4'd4:
if ( m==1 || n==cols ) a13 <= 0;
else a13 <= rom_data_win;
4'd5:
if ( n==1 ) a21 <= 0;
else a21 <= rom_data_win;
4'd6:
a22 <= rom_data_win;
4'd7:
if ( n==cols ) a23 <= 0;
else a23 <= rom_data_win;
4'd8:
if ( m==cols || n==1 ) a31 <= 0;
else a31 <= rom_data_win;
4'd9:
if ( m==cols ) a32 <= 0;
else a32 <= rom_data_win;
4'd10:
if ( m==cols || n==cols ) a33 <= 0;
else a33 <= rom_data_win;
default:;
endcase
/***********************************************************************************/
assign win_data_done_sig = isWinDone;
assign rom_addr_sig = rom_addr;
assign data_out0 = a11;
assign data_out1 = a12;
assign data_out2 = a13;
assign data_out3 = a21;
assign data_out4 = a22;
assign data_out5 = a23;
assign data_out6 = a31;
assign data_out7 = a32;
assign data_out8 = a33;
/***********************************************************************************/
endmodule
3)計數器控制模組,主要用於獲得中心畫素點的地址資訊。
(1)系統模組開始訊號之後開始獲取第一個中心畫素點,注意初始化訊號值和系統開始的訊號值的區別;
(2)該時刻得到的的資料將在下一個時刻產生結果,該時刻的資料並沒有改變;
(3)注意中心畫素點的行、列位置資訊的計算;
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09:28:59 05/18/2016
// Design Name:
// Module Name: counter_ctrl
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module counter_ctrl(
CLK,
RSTn,
start_sig, //input-from top
nxt_pix_sig, //input-from --start next center point pixel
cols,
column_addr_sig, //output
row_addr_sig, //output-to
pix_done_sig //output-to
);
input CLK;
input RSTn;
input start_sig;
input nxt_pix_sig;
input [9:0] cols;
output pix_done_sig;
output [9:0] column_addr_sig;
output [9:0] row_addr_sig;
/**********************************************************************************/
reg isCtrlDone;
//reg isWinStart;
reg [17:0] imk; //The k-th pixel of the image
reg [9:0] row_addr; // The row of the centeral pixel
reg [9:0] column_addr; // The column of the centeral pixel
reg start_sig_d;
wire start_sig_rising_vld;
always @ (posedge CLK or negedge RSTn) //Asynchronous reset
if (!RSTn)
start_sig_d <= 0;
else
start_sig_d <= start_sig;
assign start_sig_rising_vld = start_sig & (~start_sig_d);
always @ (posedge CLK or negedge RSTn) //Asynchronous reset
if (!RSTn)
begin
imk <= 18'b0;
column_addr <= 10'b0;
row_addr <= 10'b0;
isCtrlDone <= 1'b0;
end
else if (start_sig_rising_vld)
begin
imk <= 18'b1;
column_addr <= 10'b1;
row_addr <= 10'b1;
isCtrlDone <= 1'b1;
end
else if ( nxt_pix_sig )
begin
imk <= imk + 1'b1;
row_addr <= imk / cols + 1;
column_addr <= imk % cols + 1;
isCtrlDone <= 1'b1;
end
else isCtrlDone <= 1'b0;
/***********************************************************************************/
assign row_addr_sig = row_addr;
assign column_addr_sig = column_addr;
assign pix_done_sig = isCtrlDone;
/***********************************************************************************/
endmodule
4) 3*3中值濾波模組
功能:得到某一中心畫素點的3*3滑窗區域的灰度值的中值,作為中心畫素點的值;
中值濾波原理,網上有很多,大家可以檢視一下。
本專案採用的是快速中值濾波的方法。
(1)若是3*3視窗生成模組完成之後就計算下一個中心畫素點,需要將該中心畫素點的視窗元素鎖存起來,以防計算過程中將這些元素掩蓋,不能正確進行中值濾波的計算;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
a11 <= 0;
a12 <= 0;
a13 <= 0;
a21 <= 0;
a22 <= 0;
a23 <= 0;
a31 <= 0;
a32 <= 0;
a33 <= 0;
end
else if (win_data_sig)
begin
a11 <= data_in0;
a12 <= data_in1;
a13 <= data_in2;
a21 <= data_in3;
a22 <= data_in4;
a23 <= data_in5;
a31 <= data_in6;
a32 <= data_in7;
a33 <= data_in8;
end
(2)需要在時序的有效區域內進行計算,怎麼設計訊號的有效性;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
cal_vld <= 1'b0;
else if( win_data_sig )
cal_vld <= 1'b1;
else if( i==3'd3 )
cal_vld <= 0;
(3)仿順序操作可以分開進行;每一個時刻只進行一個操作,這樣可能更明瞭(程式碼中沒有這樣做);
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
i <= 3'd0;
else if( cal_vld & ( i!=3 ) )
i <= i + 1;
else
i <= 0;
(4)verilog程式設計呼叫函式的方法,指出輸入訊號,函式內可以使用其他定義宣告的訊號,最後的輸出訊號作為呼叫函式的結果(突然想起來,如果輸出訊號有多個元素呢,又該怎麼辦呢?大家可以想想);
function [7:0] max;//if the data is signed number, please add the char signed behind key function;
input [7:0] a, b, c;
begin
max = (((a >= b) ? a : b) >= c ) ? ((a >= b) ? a : b) : c;
end
endfunction
該模組的程式碼:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09:28:20 05/18/2016
// Design Name:
// Module Name: medfilter3by3
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module medfilter3by3(
CLK,
RSTn,
win_data_sig, //input-from module of win3by3_gen;
medfilt_done_sig, //output-to top;
data_in0, //input-from module of win3by3_gen;
data_in1,
data_in2,
data_in3,
data_in4,
data_in5,
data_in6,
data_in7,
data_in8,
medfilt_data_out //output-to top;
);
input CLK;
input RSTn;
input win_data_sig;
input [7:0] data_in0; //output-to ;
input [7:0] data_in1;
input [7:0] data_in2;
input [7:0] data_in3;
input [7:0] data_in4;
input [7:0] data_in5;
input [7:0] data_in6;
input [7:0] data_in7;
input [7:0] data_in8;
output medfilt_done_sig;
output [7:0] medfilt_data_out;
/******************************************************************************/
reg [7:0] a11;
reg [7:0] a12;
reg [7:0] a13;
reg [7:0] a21;
reg [7:0] a22;
reg [7:0] a23;
reg [7:0] a31;
reg [7:0] a32;
reg [7:0] a33;
reg [7:0] b11;
reg [7:0] b12;
reg [7:0] b13;
reg [7:0] b21;
reg [7:0] b22;
reg [7:0] b23;
reg [7:0] b31;
reg [7:0] b32;
reg [7:0] b33;
reg [7:0] c11;
reg [7:0] c12;
reg [7:0] c13;
reg [7:0] c21;
reg [7:0] c22;
reg [7:0] c23;
reg [7:0] c31;
reg [7:0] c32;
reg [7:0] c33;
reg [2:0] i;
reg [7:0] medfilt_data;
reg filt_done;
reg cal_vld;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
a11 <= 0;
a12 <= 0;
a13 <= 0;
a21 <= 0;
a22 <= 0;
a23 <= 0;
a31 <= 0;
a32 <= 0;
a33 <= 0;
end
else if (win_data_sig)
begin
a11 <= data_in0;
a12 <= data_in1;
a13 <= data_in2;
a21 <= data_in3;
a22 <= data_in4;
a23 <= data_in5;
a31 <= data_in6;
a32 <= data_in7;
a33 <= data_in8;
end
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
i <= 3'd0;
else if( cal_vld & ( i!=3 ) )
i <= i + 1;
else
i <= 0;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
cal_vld <= 1'b0;
else if( win_data_sig )
cal_vld <= 1'b1;
else if( i==3'd3 )
cal_vld <= 0;
always @ ( posedge CLK or negedge RSTn )
if (!RSTn)
begin
filt_done <= 1'b0;
b11 <= 0;
b12 <= 0;
b13 <= 0;
b21 <= 0;
b22 <= 0;
b23 <= 0;
b31 <= 0;
b32 <= 0;
b33 <= 0;
c11 <= 0;
c12 <= 0;
c13 <= 0;
c21 <= 0;
c22 <= 0;
c23 <= 0;
c31 <= 0;
c32 <= 0;
c33 <= 0;
medfilt_data <= 0;
end
else if( cal_vld )
case(i)
3'd0:
begin
b11 <= max(a11, a21, a31);
b12 <= max(a12, a22, a32);
b13 <= max(a13, a23, a33);
b21 <= med(a11, a21, a31);
b22 <= med(a12, a22, a32);
b23 <= med(a13, a23, a33);
b31 <= min(a11, a21, a31);
b32 <= min(a12, a22, a32);
b33 <= min(a13, a23, a33);
end
3'd1:
begin
c31 <= max(b31, b32, b33);
c22 <= med(b21, b22, b23);
c13 <= min(b11, b12, b13);
end
3'd2:
begin
medfilt_data <= med(c13, c22, c31);
filt_done<=1'b1;
end
3'd3:
filt_done <= 1'b0;
default:;
endcase
/*******************************************************************************/
function [7:0] max;//if the data is signed number, please add the char signed behind key function;
input [7:0] a, b, c;
begin
max = (((a >= b) ? a : b) >= c ) ? ((a >= b) ? a : b) : c;
end
endfunction
function [7:0] med;
input [7:0] a, b, c;
begin
med = a < b ? (b < c ? b : a < c ? c : a) : (b > c ? b : a > c ? c : a);
end
endfunction
function [7:0] min;
input [7:0] a, b, c;
begin
min= (((a <= b) ? a : b) <= c ) ? ((a <= b) ? a : b) : c;
end
endfunction
/*******************************************************************************/
assign medfilt_data_out = medfilt_data;
assign medfilt_done_sig = filt_done;
/*******************************************************************************/
endmodule
5)頂層模組,用於將低層的各個功能/控制模組銜接起來,得到結果;
注意輸入輸出訊號,以及不同模組之間是如何進行連線的。
訊號的名稱儘量有其特別的意義,不要重複使用同一個訊號名稱,容易造成混亂;
區別wire和reg型別資料的使用情況;
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 09:29:33 05/18/2016
// Design Name:
// Module Name: medfilter2
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module medfilter2
(
CLK,
RSTn,
Start_sig,
Done_sig,
Data_out
);
input CLK;
input RSTn;
input Start_sig;
output Done_sig;
output [7:0] Data_out;
/********************************************************************/
wire [17:0] rom_addr; //
wire [7:0] rom_data; //
rom_512by512 rom_512by512_inst
(
.clka(CLK), //input clka;
.addra(rom_addr), //input-from;
.douta(rom_data) //output-to ;
);
/******************************************************************************/
//wire [7:0] win_data[8:0];
wire [7:0] data_out0; //output-to ;
wire [7:0] data_out1;
wire [7:0] data_out2;
wire [7:0] data_out3;
wire [7:0] data_out4;
wire [7:0] data_out5;
wire [7:0] data_out6;
wire [7:0] data_out7;
wire [7:0] data_out8;
wire win_done_sig;
wire [9:0] column_addr_sig;
wire [9:0] row_addr_sig;
win3by3_gen win3by3_gen_inst (
.CLK(CLK),
.RSTn(RSTn),
.center_pix_sig(win_start_sig), //input-from ;
.cols(10'd512), // the column numbers of the input image
.rows(10'd512), // the row numbers of the input image
.rom_data_win(rom_data), //input-from ;
.column_addr_sig(column_addr_sig), //input-from ; //output [9 : 0] addra;
.row_addr_sig(row_addr_sig), //input-from ; //output [9 : 0] addra;
.rom_addr_sig(rom_addr), //output-to ;
.data_out0(data_out0), //output-to ;
.data_out1(data_out1),
.data_out2(data_out2),
.data_out3(data_out3),
.data_out4(data_out4),
.data_out5(data_out5),
.data_out6(data_out6),
.data_out7(data_out7),
.data_out8(data_out8),
.win_data_done_sig(win_done_sig) //output-to U4/U3;
);
/******************************************************************************/
counter_ctrl counter_ctrl_inst(
.CLK(CLK),
.RSTn(RSTn),
.start_sig(Start_sig), //input-from top
.nxt_pix_sig(win_done_sig), //input-from
.cols(10'd512),
.column_addr_sig(column_addr_sig), //output-to
.row_addr_sig(row_addr_sig), //output-to
.pix_done_sig(win_start_sig) //output-to
);
/*****************************************************************************/
wire medfilt_done_sig;
wire [7:0] medfilt_data_wire;
medfilter3by3 medfilter3by3_inst
(
.CLK(CLK),
.RSTn(RSTn),
.win_data_sig(win_done_sig), //input-from;
.medfilt_done_sig(medfilt_done_sig), //output-to;
.data_in0(data_out0), //input-from ;
.data_in1(data_out1),
.data_in2(data_out2),
.data_in3(data_out3),
.data_in4(data_out4),
.data_in5(data_out5),
.data_in6(data_out6),
.data_in7(data_out7),
.data_in8(data_out8),
.medfilt_data_out(medfilt_data_wire) //output-to top;
);
/*********************************************************************/
wire Done_sig;
wire [7:0] Data_out;
assign Done_sig = medfilt_done_sig;
assign Data_out = medfilt_data_wire;
/**********************************************************************/
endmodule
6)測試模組
如何將資料寫入檔案,需要定義檔案的名稱和型別;
integer fouti;
需要在初始化部分開啟檔案:
fouti = $fopen("medfilter2_re.txt");
程式碼如下:
`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 13:57:14 05/24/2016
// Design Name: medfilter2
// Module Name: E:/stereo_match_pro/stereo_match_FPGA0518/medfilter_tb.v
// Project Name: stereo_match_FPGA0518
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: medfilter2
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
////////////////////////////////////////////////////////////////////////////////
module medfilter_tb;
// Inputs
reg CLK;
reg RSTn;
reg Start_sig;
reg [18:0] pix_cnt; //512*512=262144=100,0000,0000,0000,0000
// Outputs
wire Done_sig;
wire [7:0] Data_out;
integer fouti;
// Instantiate the Unit Under Test (UUT)
medfilter2 uut (
.CLK(CLK),
.RSTn(RSTn),
.Start_sig(Start_sig),
.Done_sig(Done_sig),
.Data_out(Data_out)
);
//assign Data_out = 0;
//assign Done_sig = 0;
initial begin
// Initialize Inputs
CLK = 0;
RSTn = 1;
Start_sig = 0;
fouti = $fopen("medfilter2_re.txt");
// Wait 100 ns for global reset to finish
#100; // To reset the system
// Add stimulus here
RSTn = 0;
Start_sig = 1;
pix_cnt = 0;
#100; // To start the system
// Add stimulus here
RSTn = 1;
pix_cnt = 1;
end
always #10 CLK = ~CLK;
[email protected](posedge CLK)
begin
if(Done_sig)
pix_cnt <= pix_cnt + 1;
end
[email protected](posedge CLK)
begin
if(pix_cnt == 19'd262145)
begin
Start_sig <= 0;
$display("Image Medfilter Completed!\n");
$display("The all time is %d \n",$time);
$stop;
end
end
[email protected](posedge CLK)
begin
if(Done_sig)
begin
$fwrite(fouti, "%d", Data_out, "\n");
$display("%d",pix_cnt);
end
end
endmodule
整體的程式碼就是這樣的。
4.與matlab的中值濾波結果進行比較
使用matlab程式設計基於自帶的中值濾波函式得到處理之後的影象與資料,並將verilog得到的濾波資料轉換為影象,將二者進行比較。
使用matlab自帶的中值濾波函式medfilt2生成原影象的灰度影象的濾波資料;
% mcode to median filter for one jpg image, and create a image data file
src = imread('lena.jpg');
gray = rgb2gray(src);
medfilt2im = medfilt2( gray );
[m, n] = size( medfilt2im ); % m行 n列
N = m*n; %%資料的長度,即儲存器深度。
word_len = 8; %%每個單元的佔據的位數,需自己設定
lena_gray = reshape(gray', 1, N);% 1行N列
lena_medfilt = reshape(medfilt2im', 1, N);% 1行N列
fid_gray=fopen('lena_gray.txt', 'wt'); %開啟檔案
fid_medfilt=fopen('lena_medfilt.txt', 'wt'); %開啟檔案
% fprintf(fid, 'MEMORY_INITIALIZATION_RADIX=16;\n');
% fprintf(fid, 'MEMORY_INITIALIZATION_VECTOR=\n');
for i = 1 : N-1
fprintf(fid_gray, '%d,\n', lena_gray(i));%使用%x表示十六進位制數
end
fprintf(fid_gray, '%d;\n', data(N)); %%輸出結尾,每個資料後面用逗號或者空格或者換行符隔開,最後一個數據後面加分號
fclose(fid_gray); %%關閉檔案
for i = 1 : N-1
fprintf(fid_medfilt, '%d,\n', lena_medfilt(i));%使用%x表示十六進位制數
end
fprintf(fid_medfilt, '%d;\n', lena_medfilt(N)); %%輸出結尾,每個資料後面用逗號或者空格或者換行符隔開,最後一個數據後面加分號
fclose(fid_medfilt); %%關閉檔案
將medfilt2函式和verilog產生的濾波資料轉換為影象,並與matlab直接產生的濾波影象進行對比,程式碼如下:
% code to create image data from txt file
clc;
clear all;
close all;
I_rgb = imread('lena.jpg');
subplot(2, 3, 1), imshow(I_rgb), title('lena-rgb')
I_gray = rgb2gray(I_rgb);
subplot(2, 3, 2), imshow(I_gray), title('lena-gray')
medfilt_m_load = load('.\lena_medfilt.txt');
%medfilt_m_load = load('.\lena.coe');
medfilt_v_load = load('.\medfilter2_reV1.txt'); % verilog 產生的中值濾波之後資料
medfilt2im = medfilt2( I_gray );
subplot(2, 3, 3), imshow(medfilt2im), title('lena-medfilt2')
m = 512;
n = 512;
medfilt_m = reshape(medfilt_m_load, m, n);
medfilt_v = reshape(medfilt_v_load, m, n);
medfilt_m = uint8(medfilt_m');
medfilt_v = uint8(medfilt_v');
aa = medfilt2im - medfilt_m;
bb = medfilt2im - medfilt_v;
cc = medfilt_m - medfilt_v;
subplot(2, 3, 5), imshow(medfilt_m), title('medfilt-matlab');
subplot(2, 3, 6), imshow(medfilt_v), title('medfilt-verilog');
顯示的結果如下圖所示:
結果:兩種濾波產生的影象資料完全一致,不過感覺函式直接產生的影象顏色更深一些,不知道為什麼。
這裡需要了解一下medfilt2這個函式的原理。結果資料表明,預設情況下該函式對影象邊界採用的是補0的方法進行處理的。
結論
中值濾波終於告一段落了!簡單的問題還是需要深入進去研究的,實踐的過程中你才會發現自己之前瞭解的東西是多麼的淺薄,對已知的知識掌握的是多麼的流於表面!