基于FPGA的图像处理6--形态学滤波(腐蚀,膨胀,开运算,闭运算)-最大值,最小值滤波

Github:https://github.com/zgw598243565/Maxmin-filter

6.1 背景介绍

        数学形态学是一门建立在集论基础上的学科,是几何形态学分析和描述的有利工具。数学形态学的历史可回溯到19世纪。1964年法国的Matheron和Serra在积分几何的研究成果上,将数学形态学引入图像处理邻域,并研制了基于数学形态学的图像处理系统。1982年出版的专著Image Analysis and Mathematical Morphology是数学形态学发展的重要里程碑,表明数学形态学在理论上趋于完备及应用上不断深入。数学形态学蓬勃发展,由于其并行快速,易于硬件实现,已引起了人们的广泛关注。目前,数学形态学已在计算机视觉,信号处理与图像分析,模式识别,计算方法与数据处理等方面得到了极为广泛的应用。数学形态学可以用来解决抑制噪声,特征提取,边缘检测,图像分割,形状识别,纹理分析,图像恢复与重建,图像压缩等图像处理问题。

1. 数学形态学的定义

        数学形态学是以形态结构元素为基础对图像进行分析的数学工具,它的基本思想是,用具有一定形态的结构元素度量和提取图像中的对应形状,以达到对图像分析和识别的目的。数学形态学的应用可以简化图像数据,保持它们基本的形状特征,并除去不相干的结构。数学形态学的基本运算有4个:膨胀,腐蚀,开运算和闭运算。它们在二值图像中和灰度图像中各有特点。基于这些基本运算还可以推导和组合成各种数学形态学实用算法。

2. 分类

1)二值形态学

        数学形态学中二值图像的形态变换是一种针对集合的处理过程。其形态算子的实质是表达物体或形状的集合与结构元素间的相互作用,结构元素的形状就决定了这种运算所提取的信号的形状信息。形态学图像处理是在图像中移动一个结构元素,然后将结构元素与下面的二值图像进行交,并等集合运算。二值形态膨胀与腐蚀可转化为集合的逻辑运算,算法简单,适合于并行处理,且易于硬件实现,适合于对二值图像进行图像分割,细化,抽取骨架,边缘提取,形状分析。但是,在不同的应用场合,结构元素的选择及其相应的处理算法是不一样的,对不同的目标图像需设计不同的结构元素和不同的处理算法。结构元素的大小,形状选择合适与否,将直接影响图像的形态运算结构。因此,很多学者结合自己的实际应用,提出了一系列的改进算法。

2)灰度数学形态学

        二值数学形态学可方便地推广到灰度图像空间。只是灰度数学形态学的运算对象不是集合而是图像函数。灰度数学形态学中开启和闭合运算的定义与在二值数学形态学中的定义一致。

3)模糊数学形态学

        将模糊集合理论用于数学形态学就形成了模糊形态学。模糊算子的定义不同,相应的模糊形态运算的定义也不相同,模糊性由结构元素对原图像的适应程度来决定。模糊形态学是传统数学形态学从二值逻辑向模糊逻辑的推广,与传统数学形态学有相似的计算结果和相似的代数特性。模糊形态学重点研究n维空间目标物体的形状特征和形态变换,主要应用于图像处理领域,例如模糊增强,模糊边缘检测和模糊分割等。

6.2 形态学滤波的基本应用

 1. 膨胀与腐蚀

        膨胀与腐蚀是形态学滤波的两个基本运算,能实现多种多样的功能,主要功能如下:消除噪声,分割出独立的图像元素,在图像中连接相邻的元素,寻找图像中明显的极大值和极小值区域,求出图像的梯度。

1)膨胀----

        膨胀(dilate)就是求局部最大值的操作。通过膨胀操作,会使图像中的高亮区域逐渐增大,也就是对图像中高亮的部分进行膨胀,类似于“领域扩张”。可以预见的是,效果图将会拥有比原图更大的高亮区域,亮度会有所增加,同时可以联通相邻的高亮度区域。通过膨胀我们可以将图像中的裂缝得到填补。

2)腐蚀----

         腐蚀(erode)和膨胀是一对对立的操作,因此,腐蚀就是求局部的最小值。通过腐蚀操作,会使图像中的高亮区域被腐蚀掉了,类似于“领域被蚕食”,可以预见的是腐蚀过后的图像将会拥有比原图更小的高亮区域,亮度会有所下降。同时,腐蚀操作还会连通相邻的比较暗的区域。

3)开运算----

        开运算(Opening Operation)其实就是先腐蚀后膨胀的过程。其数学表达式如下: 

        开运算一般使对象的轮廓变得光滑,断开狭窄的间断和消除细小的凸起物。因此,常用来消除小物体,在纤细点处分离物体。 

4)闭运算----

        闭运算(Closing Operation)是开运算的逆运算,其实就是先膨胀后腐蚀的过程。其数学表达式如下:

        闭运算同样可以使轮廓线变得更光滑,但是与开运算相反的是,它通常能够消除狭窄的间断和长细的沟壑,消除细小的孔洞,并填补轮廓线中的断裂。因此,常用来排除小型黑洞的场合。 

6.2 形态学滤波(最大/最小值滤波)的实现

图 1 

         图1所示为最大/最小值滤波的电路结构图。其实统计排序滤波已经实现了最大/最小值滤波的电路,只需要前2级3输入比较就能得到最大/最小值的输出了。但是,这里我使用了均值滤波的电路结构,将均值滤波电路结构中的ADD_Tree模块转换成了Compare_Tree模块,这样Data_Capture模块采样进的有效数据不是累加后的值,而是经过比较后的最大/最小值。

图 2 

        图2所示为最大/最小值的Compare_Tree模块的电路结构,3x3大小的最大/最小值滤波与3x3大小的均值滤波相似,都是经过3拍开窗操作,不同的是,最大/最小值滤波开窗操作后的9个数据是进入到比较树(compare_tree),而均值滤波是进入到累加树(add_tree)。图1中输入信号mode是用决定比较单元cmp是用来执行最大值输出还是最小值输出的。

        最大/最小值滤波器的具体实现如下所示:

 

module Compare3x3 #(
    parameter LINE_NUM = 3,
    parameter PIXEL_WIDTH = 14,
    parameter KX_WIDTH =3,
    parameter IMAGE_WIDTH = 128
)(clk,arstn,data_in,din_valid,data_out,dout_valid,mode);

function integer clogb2(input integer bit_depth);
    begin
        for(clogb2 = 0; bit_depth >0;clogb2 = clogb2 +1)
            bit_depth = bit_depth >> 1;
    end
endfunction

localparam CNT_WIDTH = clogb2(IMAGE_WIDTH-1);
localparam DATA_WIDTH = PIXEL_WIDTH*LINE_NUM;

input clk;
input arstn;
input [DATA_WIDTH-1:0]data_in;
input din_valid;
input mode;
output [PIXEL_WIDTH-1:0]data_out;
output dout_valid;

reg [PIXEL_WIDTH-1:0]k00_reg;
reg [PIXEL_WIDTH-1:0]k01_reg;
reg [PIXEL_WIDTH-1:0]k02_reg;
reg [PIXEL_WIDTH-1:0]k10_reg;
reg [PIXEL_WIDTH-1:0]k11_reg;
reg [PIXEL_WIDTH-1:0]k12_reg;
reg [PIXEL_WIDTH-1:0]k20_reg;
reg [PIXEL_WIDTH-1:0]k21_reg;
reg [PIXEL_WIDTH-1:0]k22_reg;
reg [CNT_WIDTH:0]cnt_reg;
reg [CNT_WIDTH:0]cnt;

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                k00_reg <= 0;
                k01_reg <= 0;
                k02_reg <= 0;
            end
        else
            begin
                if(din_valid)
                    begin
                        k00_reg <= data_in[PIXEL_WIDTH-1:0];
                        k01_reg <= data_in[2*PIXEL_WIDTH-1:PIXEL_WIDTH];
                        k02_reg <= data_in[3*PIXEL_WIDTH-1:2*PIXEL_WIDTH];
                    end
            end
    end

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                k10_reg <= 0;
                k11_reg <= 0;
                k12_reg <= 0;
            end
       else
            begin
                k10_reg <= k00_reg;
                k11_reg <= k01_reg;
                k12_reg <= k02_reg;
            end
    end

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                k20_reg <= 0;
                k21_reg <= 0;
                k22_reg <= 0;
            end
         else
            begin
                k20_reg <= k10_reg;
                k21_reg <= k11_reg;
                k22_reg <= k12_reg;
            end
    end
    
 /* The first compare pipe */
reg [PIXEL_WIDTH-1:0]comp_delay_00;
reg [PIXEL_WIDTH-1:0]comp_delay_01;
reg [PIXEL_WIDTH-1:0]comp_delay_02;
reg [PIXEL_WIDTH-1:0]comp_delay_03;
reg [PIXEL_WIDTH-1:0]comp_delay_04;

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                comp_delay_00 <= 0;
                comp_delay_01 <= 0;
                comp_delay_02 <= 0;
                comp_delay_03 <= 0;
                comp_delay_04 <= 0;
            end
        else
            begin
               if(mode == 1'b1)
                    begin
                        if(k00_reg > k01_reg)
                            comp_delay_00 <= k00_reg;
                        else
                            comp_delay_00 <= k01_reg;
                        if(k02_reg > k10_reg)
                            comp_delay_01 <= k02_reg;
                        else
                            comp_delay_01 <= k10_reg;
                        if(k11_reg > k12_reg)
                            comp_delay_02 <= k11_reg;
                        else
                            comp_delay_02 <= k12_reg;
                        if(k20_reg > k21_reg)
                            comp_delay_03 <= k20_reg;
                        else
                            comp_delay_03 <= k21_reg;
                        comp_delay_04 <= k22_reg;    
                    end
               else
                    begin
                        if(k00_reg > k01_reg)
                             comp_delay_00 <= k01_reg;
                        else
                            comp_delay_00 <= k00_reg;
                        if(k02_reg > k10_reg)
                            comp_delay_01 <= k10_reg;
                        else
                            comp_delay_01 <= k02_reg;
                        if(k11_reg > k12_reg)
                            comp_delay_02 <= k12_reg;
                        else
                            comp_delay_02 <= k11_reg;
                        if(k20_reg > k21_reg)
                            comp_delay_03 <= k21_reg;
                        else
                            comp_delay_03 <= k20_reg;
                        comp_delay_04 <= k22_reg;                    
                    end
            end
    end
    
/* The second compare pipe */
reg [PIXEL_WIDTH-1:0]comp_delay_10;
reg [PIXEL_WIDTH-1:0]comp_delay_11;
reg [PIXEL_WIDTH-1:0]comp_delay_12;

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                comp_delay_10 <= 0;
                comp_delay_11 <= 0;
                comp_delay_12 <= 0;
            end
        else
            begin
                if(mode == 1'b1)
                    begin
                        if(comp_delay_00 > comp_delay_01)
                            comp_delay_10 <= comp_delay_00;
                        else
                            comp_delay_10 <= comp_delay_01;
                        
                        if(comp_delay_02 > comp_delay_03)
                            comp_delay_11 <= comp_delay_02;
                        else
                            comp_delay_11 <= comp_delay_03;
                        comp_delay_12 <= comp_delay_04;
                    end
                else
                    begin
                         if(comp_delay_00 > comp_delay_01)
                            comp_delay_10 <= comp_delay_01;
                         else
                            comp_delay_10 <= comp_delay_00;
                    
                         if(comp_delay_02 > comp_delay_03)
                            comp_delay_11 <= comp_delay_03;
                         else
                            comp_delay_11 <= comp_delay_02;
                         comp_delay_12 <= comp_delay_04;                   
                    end
            end
    end

/* The Third compare pipe */
reg [PIXEL_WIDTH-1:0]comp_delay_20;
reg [PIXEL_WIDTH-1:0]comp_delay_21;

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                comp_delay_20 <= 0;
                comp_delay_21 <= 0;
            end
        else
            begin
                if(mode == 1'b1)
                    begin
                        if(comp_delay_10 > comp_delay_11)
                           comp_delay_20 <= comp_delay_10;
                        else
                            comp_delay_20 <= comp_delay_11;
                        comp_delay_21 <= comp_delay_12;
                    end
                else
                    begin
                        if(comp_delay_10 > comp_delay_11)
                            comp_delay_20 <= comp_delay_11;
                        else
                            comp_delay_20 <= comp_delay_10;
                        comp_delay_21 <= comp_delay_12;
                    end
            end
    end

/* The Fourth compare pipe */
reg [PIXEL_WIDTH-1:0]comp_delay_30;            
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            comp_delay_30 <= 0;
        else
            begin
                if(mode == 1'b1)
                    begin
                        if(comp_delay_20 > comp_delay_21)
                            comp_delay_30 <= comp_delay_20;
                        else
                            comp_delay_30 <= comp_delay_21;
                    end
                else
                    begin
                        if(comp_delay_20 > comp_delay_21)
                            comp_delay_30 <= comp_delay_21;
                        else
                            comp_delay_30 <= comp_delay_20;
                    end
            end
    end

assign data_out = comp_delay_30;

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            cnt_reg <= 0;
        else
            cnt_reg <= cnt;
    end

always@(*)
    begin
        if(din_valid)
            begin 
                if(cnt_reg == IMAGE_WIDTH - 1)
                    cnt = 0;
                else
                    cnt = cnt_reg + 1'b1;  
            end
        else
            cnt = cnt_reg;
    end   

/* dout_valid pipe*/
reg tvalid;
reg tvalid_delay_0;
reg tvalid_delay_1;
reg tvalid_delay_2;
reg tvalid_delay_3;
always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
           tvalid <= 0;
        else 
            begin
                if(cnt == KX_WIDTH)
                    tvalid <= 1'b1;
                else if(cnt_reg == IMAGE_WIDTH - 1)
                    tvalid <= 1'b0;
                else
                    tvalid <= tvalid;
            end  
    end

always@(posedge clk or negedge arstn)
    begin
        if(~arstn)
            begin
                tvalid_delay_0 <= 0;
                tvalid_delay_1 <= 0;
                tvalid_delay_2 <= 0;
                tvalid_delay_3 <= 0;
            end
        else
            begin
                tvalid_delay_0 <= tvalid;
                tvalid_delay_1 <= tvalid_delay_0;
                tvalid_delay_2 <= tvalid_delay_1;
                tvalid_delay_3 <= tvalid_delay_2;
            end
    end
assign dout_valid = tvalid_delay_3;
endmodule

图 3 

图 4 

         图3和图4是最大/最小值滤波电路在最大值滤波和最小值滤波场景的仿真电路图。

6.3 形态学滤波(开/闭运算)的实现

        我们知道开运算是先腐蚀后膨胀的过程,闭运算是先膨胀后腐蚀的过程。因此,实现开/闭运算其实可以将两个最大/最小值滤波电路串联,两个分别做相反的运算即可。

 

图 5 

        如图5所示为开/闭运算的电路结构图。这里要注意的是因为做膨胀或腐蚀后的结果图,会在上,下,左,右四个边界少滤波核半径大小的值,而且图像外边界为滤波核半径大小外的图像数据没有被滤波运算值替代,因此,边界值会保留。所以,进行一次最大/最小值滤波后,在数据进入下一个最大/最小值滤波电路中时,需要进行像素填充,如果采用的滤波核大小是3x3的,则卷积核半径就是1,因此,填充像素个数为2*(WIDTH+HEGIHT)个。

 

 

 

 

      

  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值