基于FPGA的图像中值滤波算法实现方案

描述

一、中值滤波算法原理

中值滤波算法简单来说就是:通过对3x3窗口中的数据进行排序,最终获得中值。

对于待处理的像素,我们选择一个3x3的窗口模板,该窗口内的像素为待处理像素的邻近像素,对窗口内的像素分别按行列排序,最终计算出中值,用该中值代替原像素值,实现中值滤波。

FPGA

二、基于FPGA设计中值滤波

1.3x3图像窗口生成

卷积是图像处理 中很常见的一种操作,3x3是最常见的窗口大小。

FPGA

如果像素是一个个来的,要想实现3x3卷积,就得同时获取一个像素和它周围的8个像素,将输入像素缓存2行,这样就能同时获取3行的像素输入,此时再将这3个并行输入的像素移位进3x3窗口,就获得了3x3卷积模板,如图:

FPGA

这里要注意,输入像素此时作为第三行数据输入3x3窗口,最下面的行缓存输出的才是第一行像素,上图窗口的右下角是3x3卷积模板的左上角,窗口的左上角是3x3卷积模板的右下角。

实现两行缓存并获取3x3卷积窗口,用shift-ram是最简单的实现方法。

1.2 shift-ram

1.2.1shift_ram简介

shift-ram是一个ip核,quartus13.0中叫做Shift register(RAM based)

普通shift-ram如图:

FPGA

带taps的shift-ram:

FPGA

其实带taps的shift-ram就是多个普通shift-ram组合,使用带taps的shift-ram可以轻松实现行缓存,设定taps数量为2,taps间隔为一行的像素数(此处为640),即可缓存两行。之后将这两个taps和输入像素移位进3x3窗口即可获得3x3卷积模板。

1.2.2 shift_ram配置方法

FPGA

三、程序设计

module median_filter (

input               clk     ,

input               rst_n   ,

input               din_vld ,

input       [7:0]   din     ,   //输入的图像数据

output      [7:0]   dout    ,   //输出的图像数据

output              dout_vld    

);

//打拍

reg     [3:0]   din_vld_r;

wire    [7:0]   taps0 ;

wire    [7:0]   taps1 ;

wire    [7:0]   taps2 ;

//行同步

reg     [7:0]   row0_0;

reg     [7:0]   row0_1;

reg     [7:0]   row0_2;

reg     [7:0]   row1_0;

reg     [7:0]   row1_1;

reg     [7:0]   row1_2;

reg     [7:0]   row2_0;

reg     [7:0]   row2_1;

reg     [7:0]   row2_2;

//行排列

reg     [7:0]   row0_min;

reg     [7:0]   row0_med;

reg     [7:0]   row0_max;

reg     [7:0]   row1_min;

reg     [7:0]   row1_med;

reg     [7:0]   row1_max;

reg     [7:0]   row2_min;

reg     [7:0]   row2_med;

reg     [7:0]   row2_max;

//列排列

reg     [7:0]   col0_min;

reg     [7:0]   col0_med;

reg     [7:0]   col0_max;

reg     [7:0]   col1_min;

reg     [7:0]   col1_med;

reg     [7:0]   col1_max;

reg     [7:0]   col2_min;

reg     [7:0]   col2_med;

reg     [7:0]   col2_max;

//取出max列的min,med列的med,min列的max

reg     [7:0]   data_max;

reg     [7:0]   data_med;

reg     [7:0]   data_min;

/**************************************************************

四级流水(打四拍)        

**************************************************************/

always@(posedge clk or negedge rst_n)

if(!rst_n)

din_vld_r <= 4'd0;

else

din_vld_r <= {din_vld_r[2:0],din_vld};

/**************************************************************

shift_ram(3x3)模块             

**************************************************************/

shift_ramshift_ram_inst (

.aclr       ( !rst_n    ),

.clken      ( din_vld   ),

.clock      ( clk       ),

.shiftin    ( din       ),

.shiftout   (           ),

.taps0x     ( taps0     ),

.taps1x     ( taps1     ),

.taps2x     ( taps2     )

);

/**************************************************************

第一级流水       

**************************************************************/

//缓存3行数据

always@(posedge clk or negedge rst_n)

if(!rst_n) begin

row0_0 <= 'd0; row0_1 <= 'd0; row0_2 <= 'd0;

row1_0 <= 'd0; row1_1 <= 'd0; row1_2 <= 'd0;

row2_0 <= 'd0; row2_1 <= 'd0; row2_2 <= 'd0;

end

else if(din_vld_r[0]) begin

row0_0 <= taps0; row0_1 <= row0_0; row0_2 <= row0_1;

row1_0 <= taps1; row1_1 <= row1_0; row1_2 <= row1_1;

row2_0 <= taps2; row2_1 <= row2_0; row2_2 <= row2_1;

end

/**************************************************************

第二级流水           

**************************************************************/   

//三行分别排序

always@(posedge clk or negedge rst_n)

if(!rst_n) begin

row0_min <= 'd0;row0_med <= 'd0;row0_max <= 'd0;

row1_min <= 'd0;row1_med <= 'd0;row1_max <= 'd0;

row2_min <= 'd0;row2_med <= 'd0;row2_max <= 'd0;

end

else if(din_vld_r[1]) begin

COMPARE(row0_0,row0_1,row0_2,row0_max,row0_med,row0_min);

COMPARE(row1_0,row1_1,row1_2,row1_max,row1_med,row1_min);

COMPARE(row2_0,row2_1,row2_2,row2_max,row2_med,row2_min);

end

/**************************************************************

第三级流水          

**************************************************************/

//每一行排完后,取出

//第一列的最小值

//第二列的中间值

//第三列的最大值

always@(posedge clk or negedge rst_n)

if(!rst_n) begin

col0_min <= 'd0;

col1_med <= 'd0;

col0_max <= 'd0;

end

else if(din_vld_r[2]) begin

COMPARE(row0_max,row1_max,row2_max,col0_max,col0_med,col0_min);

COMPARE(row0_med,row1_med,row2_med,col1_max,col1_med,col1_min);

COMPARE(row0_min,row1_min,row2_min,col2_max,col2_med,col2_min);

end

/**************************************************************

第四级流水           

**************************************************************/

//得到最终的中值

always@(posedge clk or negedge rst_n)

if(!rst_n) begin

data_max <= 'd0;

data_med <= 'd0;

data_min <= 'd0;

end

else if(din_vld_r[3])begin

COMPARE(col0_min,col1_med,col2_max,data_max,data_med,data_min);

end

//输出端口

assign  dout = data_med;

assign  dout_vld = din_vld_r[3];

/**************************************************************

COMPARE任务          

**************************************************************/  

//用于比较三个数的大小并排列  

task COMPARE;

input      [7:0]    data1   ;

input      [7:0]    data2   ;

input      [7:0]    data3   ;

output     [7:0]    max     ;

output     [7:0]    mid     ;

output     [7:0]    min     ;

begin

//max

if(data1 >= data2 && data1 >= data3)

max = data1;

else if(data2 >= data1 && data2 >= data3)

max = data2;

else

max = data3;

//med

if((data1 >= data2 && data1 <= data3) || (data1 >= data3 && data1 <= data2))

mid = data1;

else if((data2 >= data1 && data2 <= data3) || (data2 >= data3 && data2 <= data1))

mid = data2;

else

mid = data3;

//min

if(data1 <= data2 && data1 <= data3)

min = data1;

else if(data2 <= data1 && data2 <= data3)

min = data2;

else

min = data3;              

end

endtask

endmodule

四、仿真测试

FPGA

FPGA

4.1 代码逻辑功能仿真

首先对设计的代码单独进行仿真观察波形,看中值滤波功能是否正常执行

4.1.1 median_filter_tb

4.1.1.1 测试代码

`timescale 1ns/1ps

module  median_filter_tb();

parameter CLK_CYCLE = 20;

regsys_clk,sys_rst_n;

reg din_vld;

reg [7:0]   din;

always #(CLK_CYCLE/2) sys_clk = ~sys_clk;

initial begin

sys_clk = 1'b1;

sys_rst_n = 1'b0;

#(CLK_CYCLE*2);

sys_rst_n = 1'b1;

end

median_filter median_filter_tb(

/* input             */   .clk     (sys_clk),

/* input             */   .rst_n   (sys_rst_n),

/* input             */   .din_vld (din_vld),

/* input       [7:0] */   .din     (din),

/* output      [7:0] */   .dout    (),

/* output            */   .dout_vld() 

);

integer i;

initial begin

din = 8'b0;

din_vld =1'b0;

#(CLK_CYCLE*20);

for(i=0;i<500;i=i+1)begin

din_vld = 1'b1;

din = {$random}%100;

@(posedge sys_clk);

end

din_vld = 1'b0;

#(CLK_CYCLE*200)

$stop(2);

end

endmodule

一键获取完整项目代码

4.1.1.1 仿真波形

观察每级流水波形,看每级流水功能是否正常。若哪级流水错误,则去修改该级流水代码逻辑,直到功能正常。

4.1.2 test_image

4.1.2.1 测试代码

仿真代码中涉及的系统函数可参考:Verilog 系统函数

`timescale 1ns/1ps

module test_image();

parameter CLK_CYCLE = 20;

regclk,rst_n;

always #(CLK_CYCLE/2) clk = ~clk;

initial begin

clk = 1'b1;

rst_n = 1'b0;

#(CLK_CYCLE*2);

rst_n = 1'b1;

end

reg [7:0]   din;

reg         din_vld;

wire [7:0]  dout;

wire        dout_vld;

median_filter med_filter_inst(

/* input             */   .clk     (clk),

/* input             */   .rst_n   (rst_n),

/* input             */   .din_vld (din_vld),

/* input       [7:0] */   .din     (din    ),

/* output      [7:0] */   .dout    (dout    ),

/* output            */   .dout_vld(dout_vld) 

);

integer bmp_width;//图像宽度

integer bmp_high;//图像高度

integer bmp_size;//图像尺寸

integer start_index;//图像像素点起始位

//bmp file id

integer bmp_file_id;

integer bmp_dout_id;

integer dout_txt_id;

integer h;//文件句柄

reg [7:0]   rd_data     [0:921600];//bmp文件图片大小

reg [7:0]   dout_data   [0:921600];

//写操作

reg[23:0]wr_data;

integer i = 0;

integer index,index0;

initial begin

din_vld = 0;

#(CLK_CYCLE*10)

//打开原始图像$fopen("原始图像地址,文件夹间用\隔开","命令码")

bmp_file_id = $fopen("E:\Material\IntelFPGA\test\test6\median_filter\sim\in_bmp.bmp","rb"); 

//打开输出图像

bmp_dout_id = $fopen("E:\Material\IntelFPGA\test\test6\median_filter\sim\out_bmp.bmp","wb");

//打开输出数据

dout_txt_id = $fopen("E:\Material\IntelFPGA\test\test6\median_filter\sim\out.img.txt","w+");

//读取bmp文件

h = $fread(rd_data,bmp_file_id);

// 图像宽度

bmp_width = {rd_data[21], rd_data[20], rd_data[19], rd_data[18]};

// 图像高度

bmp_high = {rd_data[25], rd_data[24], rd_data[23], rd_data[22]};

// 像素起始位置

start_index = {rd_data[13], rd_data[12], rd_data[11], rd_data[10]};

// 图像尺寸

bmp_size = {rd_data[5], rd_data[4], rd_data[3], rd_data[2]};

// bmp_size = 921600;

$fclose(bmp_file_id);

index = start_index;

//重复1280次,1280个像素点

repeat(1280)begin

#(CLK_CYCLE*1);

din = rd_data[index];

din_vld = 1;

index = index + 1;

end

repeat(bmp_size-1280)begin

#(CLK_CYCLE*1);

dout_data[index-1280] = dout;

din = rd_data[index];

index = index + 1;

end

din_vld = 0;

// repeat(1280)begin

// #(CLK_CYCLE*1);

// dout_data[index-1280] = dout;

// din = rd_data[index];

// index = index + 1;

// end

//输出BMP

for(i = 0; i < bmp_size; i = i + 1)begin

if(i < start_index)

$fwrite(bmp_dout_id, "%c", rd_data[i]);//注意参数%c

else 

$fwrite(bmp_dout_id, "%c", dout_data[i]);

end

$fclose(bmp_dout_id);

//输出txt,只存像素点

for(index0 = start_index; index0 < bmp_size-2; index0 = index0 + 3)begin

wr_data = {dout_data[index0 + 2], dout_data[index0 + 1], dout_data[index0]};

$fwrite(dout_txt_id, "%d,", wr_data[7:0]);

$fwrite(dout_txt_id, "%d,", wr_data[15:8]);

$fwrite(dout_txt_id, "%d ", wr_data[23:16]);

end

$fclose(dout_txt_id);

$stop;

end

endmodule

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分