基于FPGA灰度图像的膨胀算法的实现 1 背景知识
腐蚀与膨胀是形态学滤波的两个基本运算,通过腐蚀和膨胀两种运算可以实现多种功能,主要如下:
(1)消除噪声; (2)分割出独立的图像元素; (3)在图像中连接相邻的元素; (4)寻找图像中明显的极大值和极小值区域; (5)求出图像的梯度。 图1 腐蚀膨胀示意图 图1 a为大小为448X425像素的灰度级X射线图像;b使用半径为2个像素的圆盘形结构元对图像的腐蚀结果;c用相同的结构元对图像的膨胀结果。原图有Lixi公司提供。 1)形态学滤波之膨胀 膨胀(dialate)就是求局部最大值的操作。 从数学角度来看就是将图像f和核(结构元)b进行卷积的一个过程。 当b的原点位于(x,y)处时,用一个平坦的结构元b在(x,y)处对图像f的膨胀,定义为图像f中与b重合区域的最大值,即:
为了方便起见,将膨胀操作记为:
(x,y)表示当前输入图像的行列坐标;
f(x,y)表示坐标点(x,y)处的图像像素值;
g(x,y)表示坐标点(x,y)处的滤波结果;
(s,t)表示作用域。
2 matlab仿真灰度图像的膨胀
Matlab膨胀源码:
%%imagedilate clc clearall img_a= imread('flower.bmp'); figure,imshow(img_a); title('img_a rgb'); img_b= rgb2gray(img_a); figure,imshow(img_b); title('img_b gary'); a= [1,1,1; 1,1,1; 1,1,1]; %structural element b= [1,1,1,1,1; 1,1,1,1,1; 1,1,1,1,1; 1,1,1,1,1; 1,1,1,1,1]; c = [1,1,1,1,1,1,1; 1,1,1,1,1,1,1; 1,1,1,1,1,1,1; 1,1,1,1,1,1,1; 1,1,1,1,1,1,1; 1,1,1,1,1,1,1; 1,1,1,1,1,1,1]; img_c= imdilate(img_b,a); figure,imshow(img_c); title('img_c 3x3'); img_d= imdilate(img_b,b); figure,imshow(img_d); title('img_d 5x5'); img_e= imdilate(img_b,c); figure,imshow(img_e); title('img_e 7x7');
3.1 膨胀模块的设计
1)比较子模块
2)一维形态学膨胀子模块
3)二维形态学膨胀子模块
(1)比较子模块
为了代码更好的移植,我们将比较子模块设计为独立的子模块。
Erode:输出俩个数据的较小值。
比较子模块源码:
/*
Module name: minmax.v
Description:
Data: 2018/03/14
Engineer: lipu
e-mail: 137194782@qq.com
*/
`timescale1ns/1ps
moduleminmax(
clk, //pixel clock
rst_n,
data_valid,
din,
din_r,
dout_min,
dout_max
);
parameterWIDTH = 8; // data width 8 bit
parameterUSE_REG = 1; // USE_REG = 1Data delay 1ns output. USE_REG = 0 Don'tdelay.
input clk; //pixel clock
input rst_n;
input data_valid;
input [WIDTH-1:0] din;
input [WIDTH-1:0] din_r;
output[WIDTH-1:0] dout_min;
output[WIDTH-1:0] dout_max;
wireminmax_flag;
wire[WIDTH-1:0] min_temp;
wire[WIDTH-1:0] max_temp;
reg [WIDTH-1:0] min_reg;
reg [WIDTH-1:0] max_reg;
// min or max flag
assignminmax_flag = (din > din_r) ? 1'b1:1'b0;
// min
assignmin_temp = (minmax_flag == 1'b1) ? din_r : din;
// max
assignmax_temp = (minmax_flag == 1'b1) ? din : din_r;
// USE_REG == 1
generate
if(USE_REG== 1)
begin: MAP0
always @(posedge clk) begin
if(data_valid) begin
min_reg <= #1 min_temp;
max_reg <= #1 max_temp;
end
end
assign dout_min = min_reg;
assign dout_max = max_reg;
end
endgenerate
//USE_REG == 0
generate
if(~(USE_REG== 1))
begin: MAP1
assign dout_min = min_temp;
assign dout_max = max_temp;
end
endgenerate
endmodule
比较子模块仿真源码:
/*
Module name: minmax_tb.v
Description:
*/
`timescale1ns/1ps
`defineWIDTH 8
`defineCLK_PERIOD 10
moduleminmax_tb();
reg clk; //pixel clock
reg rst_n;
reg data_valid;
reg [`WIDTH-1:0] din;
reg [`WIDTH-1:0] din_r;
wire [`WIDTH-1:0] dout_min;
wire [`WIDTH-1:0] dout_max;
minmaxminmax_inst(
.clk(clk), //pixel clock
.rst_n(rst_n),
.data_valid(data_valid),
.din(din),
.din_r(din_r),
.dout_min(dout_min),
.dout_max(dout_max)
);
initial begin
clk = 0;
rst_n = 0;
data_valid = 0;
#(`CLK_PERIOD*10);
rst_n = 1;
#(`CLK_PERIOD*10);
data_valid = 1;
#(`CLK_PERIOD*100);
data_valid = 0;
#(`CLK_PERIOD*10);
$stop;
end
always#(`CLK_PERIOD/2) clk = ~clk;
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
din <= 8'd0;
else if(data_valid)
din <= {$random}%255;
else
din <= 8'b0;
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
din_r <= 8'd0;
else if(data_valid)
din_r <= din;
else
din_r <= 8'b0;
end
Endmodule
比较子模块仿真波形:
| t
| t+1
| t+2
| t+3
| t+4
| t+5
| t+6
| t+7
| t+8
| t+9
| t+10
| din
| 128
| 42
| 232
| 92
| 72
| 77
| 95
| 37
| 216
| 184
| 198
| din_r
| X
| 128
| 42
| 232
| 92
| 72
| 77
| 95
| 37
| 216
| 184
| dout_min
| X
| X
| 42
| 42
| 92
| 72
| 72
| 77
| 37
| 37
| 184
| dout_max
| X
| X
| 128
| 232
| 232
| 92
| 77
| 95
| 95
| 216
| 216
|
当我们需要做膨胀算法时,数据取dout_max;当我们需要做腐蚀算法时,数据取dout_min。
(2)一维形态学膨胀模块设计
我们要完成对nxn窗口的腐蚀或者膨胀首先我们要做图像行的一维腐蚀或膨胀。例如我们要做3x3窗口的腐蚀或膨胀,一维形态学腐蚀或膨胀如图所示:
(3) 二维形态学腐蚀与膨胀子模块设计
形态学膨胀结果演示:
顶层源码:
////////////////////////////////////////////////////////////////
wire[15:0] rgb;
wirehs;
wirevs;
wirede;
wireo_hs;
wireo_vs;
wireo_de;
wire[7 : 0] o_y_8b;
wire[7 : 0] o_cb_8b;
wire[7 : 0] o_cr_8b;
wire[7 : 0] dout;
//assign TFT_rgb ={o_y_8b[7:3],o_y_8b[7:2],o_y_8b[7:3]}; //Y
//assign TFT_rgb ={o_cb_8b[7:3],o_cb_8b[7:2],o_cb_8b[7:3]}; //cb
//assign TFT_rgb ={o_cr_8b[7:3],o_cr_8b[7:2],o_cr_8b[7:3]}; //cr
tft_ctrltft_ctrl(
.Clk9M(clk9M), //系统输入时钟9MHZ
.Rst_n(Rst_n), //复位输入,低电平复位
.data_in({Rd_data[7:0],Rd_data[15:8]}), //待显示数据
.hcount(), //TFT行扫描计数器
.vcount(), //TFT场扫描计数器
.TFT_RGB(rgb), //TFT数据输出
.TFT_HS(hs), //TFT行同步信号
.TFT_VS(vs), //TFT场同步信号
.TFT_CLK(TFT_clk), //TFT像素时钟
.TFT_DE(de), //TFT数据使能
.TFT_begin(tft_begin),
.TFT_PWM(TFT_pwm) //TFT背光控制
);
rgb_to_ycbcr rgb_to_ycbcr_inst(
.clk(TFT_clk),
.i_r_8b({rgb[15:11],3'b0}),
.i_g_8b({rgb[10:5],2'b0}),
.i_b_8b({rgb[4:0],3'b0}),
.i_h_sync(hs),
.i_v_sync(vs),
.i_data_en(de),
.o_y_8b(o_y_8b),
.o_cb_8b(o_cb_8b),
.o_cr_8b(o_cr_8b),
.o_h_sync(o_hs),
.o_v_sync(o_vs),
.o_data_en(o_de)
);
/*
erode erode_inst(
.clk(TFT_clk),
.rst_n(Rst_n),
.hs_in(o_hs),
.vs_in(o_vs),
.din(o_y_8b),
.din_valid(o_de),
.dout(dout),
.dout_valid(TFT_de),
.hs_out(TFT_hs),
.vs_out(TFT_vs)
);
*/
dilatedilate_inst(
.clk(TFT_clk),
.rst_n(Rst_n),
.hs_in(o_hs),
.vs_in(o_vs),
.din(o_y_8b),
.din_valid(o_de),
.dout(dout),
.dout_valid(TFT_de),
.hs_out(TFT_hs),
.vs_out(TFT_vs)
);
assignTFT_rgb = {dout[7:3],dout[7:2],dout[7:3]}; //Y
//assign TFT_rgb ={o_y_8b[7:3],o_y_8b[7:2],o_y_8b[7:3]}; //Y
欢迎大家关注我的微信公众号:FPGA开源工作室:
|