形态学图像处理
2017-06-05 15:53:00 li_wen01 阅读数 2097
形态学一般是使用二值图像,进行边界提取,骨架提取,孔洞填充,角点提取,图像重建。

基本的算法:膨胀腐蚀,开操作,闭操作,击中击不中变换

几种算法进行组合,就可以实现一些非常复杂的功能,而且逻辑严密。

这里给出形态学的一般原理,以及用形态学进行边界提取,角点提取好骨架提取的原代码

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

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

(1)二值形态学
        数学形态学中二值图像的形态变换是一种针对集合的处理过程。其形态算子的实质是表达物体或形状的集合与结构元素间的相互作用,结构元素的形状就决定了这种运算所提取的信号的形状信息。形态学图像处理是在图像中移动一个结构元素,然后将结构元素与下面的二值图像进行交、并等集合运算。
        基本的形态运算是腐蚀和膨胀。
        在形态学中,结构元素是最重要最基本的概念。结构元素在形态变换中的作用相当于信号处理中的“滤波窗口”。用B(x)代表结构元素,对工作空间E中的每一点x,腐蚀和膨胀的定义为:
       
        用B(x)对E进行腐蚀的结果就是把结构元素B平移后使B包含于E的所有点构成的集合。用B(x)对E进行膨胀的结果就是把结构元素B平移后使B与E的交集非空的点构成的集合。先腐蚀后膨胀的过程称为开运算。它具有消除细小物体,在纤细处分离物体和平滑较大物体边界的作用。先膨胀后腐蚀的过程称为闭运算。它具有填充物体内细小空洞,连接邻近物体和平滑边界的作用。
        可见,二值形态膨胀与腐蚀可转化为集合的逻辑运算,算法简单,适于并行处理,且易于硬件实现,适于对二值图像进行图像分割、细化、抽取骨架、边缘提取、形状分析。但是,在不同的应用场合,结构元素的选择及其相应的处理算法是不一样的,对不同的目标图像需设计不同的结构元素和不同的处理算法。结构元素的大小、形状选择合适与否,将直接影响图像的形态运算结果。因此,很多学者结合自己的应用实际,提出了一系列的改进算法。如梁勇提出的用多方位形态学结构元素进行边缘检测算法既具有较好的边缘定位能力,又具有很好的噪声平滑能力。许超提出的以最短线段结构元素构造准圆结构元素或序列结构元素生成准圆结构元素相结合的设计方法,用于骨架的提取,可大大减少形态运算的计算量,并可同时满足尺度、平移及旋转相容性,适于对形状进行分析和描述。

(2)灰度数学形态学
        二值数学形态学可方便地推广到灰度图像空间。只是灰度数学形态学的运算对象不是集合,而是图像函数。以下设f(x,y)是输入图像,b(x,y)是结构元素。用结构元素b对输入图像y进行膨胀和腐蚀运算分别定义为:

对灰度图像的膨胀(或腐蚀)操作有两类效果:
(1)如果结构元素的值都为正的,则输出图像会比输入图像亮(或暗);
(2)根据输入图像中暗(或亮)细节的灰度值以及它们的形状相对于结构元素的关系,它们在运算中或被消减或被除掉。灰度数学形态学中开启和闭合运算的定义与在二值数学形态学中的定义一致。用b对f进行开启和闭合运算的定义为:

(3)模糊数学形态学
        将模糊集合理论用于数学形态学就形成了模糊形态学。模糊算子的定义不同,相应的模糊形态运算的定义也不相同。在此,选用Shinba的定义方法。模糊性由结构元素对原图像的适应程度来确定。用有界支撑的模糊结构元素对模糊图像的腐蚀和膨胀运算按它们的隶属函数定义为:

 

其中,x,y∈Z2代表空间坐标,ua,ub分别代表图像和结构元素的隶属函数。从(7),(8)式的结果可知,经模糊形态腐蚀膨胀运算后的隶属函数均落在[0,1]的区间内。模糊形态学是传统数学形态学从二值逻辑向模糊逻辑的推广,与传统数学形态学有相似的计算结果和相似的代数特性。模糊形态学重点研究n维空间目标物体的形状特征和形态变换,主要应用于图像处理领域,如模糊增强、模糊边缘检测、模糊分割等。

 

三 数学形态学在图像处理中的主要应用

近年来,数学形态学在图像处理方面得到了日益广泛的应用。下面主要就数学形态学在边缘检测、图像分割、图像细化以及噪声滤除等方面的应用做简要介绍。

(1)       边缘检测

边缘检测是大多数图像处理必不可少的一步,提供了物体形状的重要信息。对于二值图像,边缘检测是求一个集合A的边界,记为B(A):

 

对于灰度图像,边缘检测是求一幅图像的形态学梯度,记为g:

数学形态学运算用于边缘检测,存在着结构元素单一的问题。它对与结构元素同方向的边缘敏感,而与其不同方向的边缘(或噪声)会被平滑掉,即边缘的方向可以由结构元素的形状确定。但如果采用对称的结构元素,又会减弱对图像边缘的方向敏感性。所以在边缘检测中,可以考虑用多方位的形态结构元素,运用不同的结构元素的逻辑组合检测出不同方向的边缘。

梁勇等人构造了8个方向的多方位形态学结构元素,应用基本形态运算,得到8个方向的边缘检测结果,再把这些结果进行归一化运算、加权求和,得到最终的图像边缘。该算法在保持图像细节特征和平滑边缘等方面,取得了较好的效果。

边缘检测源代码:

[cpp] view plain copy
  1. /******************************************************************** 
  2. 形态学基本操作采用二值图像  
  3. ***********************************************************************/  
  4. #include<cv.h>  
  5. #include <highgui.h>  
  6. int main(){  
  7.     IplImage * image,*image2,*image3;  
  8.     image = cvLoadImage("E:\\image\\mapleleaf.tif", 0);  
  9.     cvNamedWindow("image",1);  
  10.     cvShowImage("image",image);  
  11.       
  12.   
  13.     /*边界提取*/  
  14.    image2 = cvCreateImage(cvSize(image->width, image->height),image->depth ,1);  
  15.     image3 = cvCreateImage(cvSize(image->width, image->height),image->depth ,1);  
  16.    int  i , j ;  
  17.    int left,right, up , down;  
  18.    int n = 2;//窗口大小为5*5  
  19.    int r,s,flag;  
  20.    unsigned char * ptr, *dst;  
  21.    for (i = 0 ; i< image->height; i++)  
  22.    {  
  23.        for (j = 0 ; j< image->width; j++)  
  24.        {  
  25.            //窗口设定  
  26.            left = j - n;  
  27.            right = j +n;  
  28.            up = i -n;  
  29.            down = i+n;  
  30.            //窗口出界处理  
  31.            if(left< 0){left = 0;}  
  32.            if(right >= image->width){right = image->width-1;}  
  33.            if(up< 0){up =0;}  
  34.            if(down >= image->height){down = image->height -1;}  
  35.   
  36.            //腐蚀处理  
  37.            dst = (unsigned char *)image2->imageData + image2->widthStep*i + j;  
  38.            flag = 1;  
  39.            for (r = up;r <= down;r++)  
  40.            {  
  41.                for (s = left ; s<= right; s++)  
  42.                {  
  43.                   ptr = (unsigned char *)image->imageData + r*image->widthStep + s;  
  44.   
  45.                   if(*ptr != 255){  
  46.                         flag = 0;  
  47.                   }  
  48.                }  
  49.            }  
  50.            if (flag == 1)  
  51.            {  
  52.                *dst = 255;  
  53.            }  
  54.            else{  
  55.                *dst = 0;  
  56.            }  
  57.   
  58.        }  
  59.    }  
  60.    cvSub(image,image2,image3,0);  
  61.    cvNamedWindow("image3",1);  
  62.    cvShowImage("image3",image3);  
  63.    cvSaveImage("mapleleafboard.bmp", image3);  
  64.    cvWaitKey(0);  
  65.     cvReleaseImage(&image);  
  66.     cvReleaseImage(&image2);  
  67.     cvReleaseImage(&image3);  
  68.     cvDestroyAllWindows();  
  69.     return 0 ;  
  70. }  
原图:



边界提取:


 

(2)       图像分割

基于数学形态学的图像分割算法是利用数学形态学变换,把复杂目标X分割成一系列互不相交的简单子集X1,X2,…,XN,即:

对目标X的分割过程可按下面的方法完成:首先求出X的最大内接“圆”X1,然后将X1从X中减去,再求X-X1的最大内接“圆”X2,…,依此类推,直到最后得到的集合为空集为止。下面以二值图像为例,介绍用数学形态学方法求解子集X1,X2,…,XN的过程。

设B为结构元素,B可以是圆、三角形、正方形等简单的几何基元,那么“简单”形状集合Xi可以用下面的公式来定义:

式中ni为一整数,用上式定义Xi分割目标,有时会产生分割过程不唯一的现象。为此可采用下面公式来定义简单集合Xi

其中Li为一个点或一条线,当Li为点时,则与(12)式定义等价。(13)式定义的简单形状Xi可由niB沿线Li移动而产生。即将“产生器”niB的中心沿“脊骨”Li移动产生。如果niB为圆,则得到的Xi称Blum带。它具有一些特殊的性质,如Xi的边界是光滑的,Xi的最大圆与其边界相切,Xi的脊骨与产生器都是唯一的等等。

有了简单形状集合Xi的定义,则目标X可按下面方法分割。首先按式(14)求出X的最大内切结构元素Xi

数学形态学用于图像分割的缺点是对边界噪声敏感。为了改善这一问题,刘志敏等人提出了基于图像最大内切圆的数学形态学形状描述图像分割算法和基于目标最小闭包结构元素的数学形态学形状描述图像分割算法,并使用该算法对二值图像进行了分割,取得了较好的效果。邓世伟等人提出一种基于数学形态学的深度图像分割算法。作者首先利用形态学算子获得分别含有阶跃边缘与屋脊边缘的凸脊和凹谷图像,然后利用控制区域生长过程得到最终的分割结果。与传统方法相比,该方法速度快,抗噪性能好。

 

(3)       形态骨架提取

形态骨架描述了物体的形状和方向信息。它具有平移不变性、逆扩张性和等幂性等性质,是一种有效的形状描述方法。二值图像A的形态骨架可以通过选定合适的结构元素B,对A进行连续腐蚀和开启运算来求取,设S(A)代表A的骨架,定义为:

蒋刚毅等人运用数学形态学方法,对交通标志的内核形状提取形态骨架函数,将其作为用于模式匹配的形状特征。A的形态骨架函数SKF(A)表示为:

SKF(X)中值较大的点对应大的n,并代表了形态骨架的主要成分,即表达了形状的主体结构;而SKF(X)中值较小的点对应小的n,是形态骨架的细节成分,与形状的边缘信息相联系。

形态骨架函数完整简洁地表达了形态骨架的所有信息,因此,根据形态骨架函数的模式匹配能够实现对不同形状物体的识别。算法具有位移不变性,因而使识别更具稳健性。

 骨架提取原代码:

[cpp] view plain copy
  1. /************************************************************************/  
  2. /* 骨架提取*/  
  3. /************************************************************************/  
  4. #include<cv.h>  
  5. #include <highgui.h>  
  6. int main(){  
  7.     IplImage* image = cvLoadImage("E:\\image\\bone.tif",0);  
  8.     cvNamedWindow("image",1);  
  9.     cvNamedWindow("image2",1);  
  10.     cvNamedWindow("image3",1);  
  11.     cvNamedWindow("image4",1);  
  12.     cvNamedWindow("image5",1);  
  13.     cvNamedWindow("image6",1);  
  14.   
  15.     cvNamedWindow("result",1);  
  16.     cvShowImage("image", image);  
  17.     //cvWaitKey(0);  
  18.     //当前图片image2  当前被腐蚀后的图片image3 被腐蚀开操作之后的图片 image5   
  19.     //image4 作为开操作的中间值 作差后的图片image6  并之后的图片result    
  20.     IplImage *image2, *image3 , *image4, *image5,*image6,*result;  
  21.     image2 = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U,1);  
  22.     image3 = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U,1);  
  23.     image5 = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U,1);  
  24.     image4 = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U,1);  
  25.     image6 = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U,1);  
  26.     result = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U,1);  
  27.       
  28.     //循环标志 flag  
  29.     bool flag = true;  
  30.     //腐蚀判断标志 flag2  
  31.     bool flag2  = true;  
  32.     int i,j,r,s;  
  33.     unsigned char * ptr,*dst;  
  34.     unsigned char B[9] = {255 ,255,255,255,255,255,255,255,255};  
  35.     //对result进行赋值 全部为0   
  36.     for (i = 0 ; i< result->height; i++)  
  37.     {  
  38.         for (j = 0 ; j< result->width ; j++)  
  39.         {  
  40.             dst = (unsigned char *)(result->imageData + result->widthStep *i +j);  
  41.             *dst = 0;  
  42.         }  
  43.     }  
  44.     image2 = cvCloneImage(image);  
  45.     cvShowImage("image2", image2);  
  46.     //cvWaitKey(0);  
  47.     while (flag)  
  48.     {  
  49.         //flag = false;  
  50.         cvShowImage("image2",image2);  
  51.         //进行腐蚀操作,开环操作 作差 并  
  52.         for (i = 0 ; i< image3->height; i++)  
  53.         {  
  54.             for (j = 0 ; j< image3->width ; j++)  
  55.             {  
  56.             dst =  (unsigned char *)(image3->imageData + i*image3->widthStep + j);  
  57.                 if ((i == 0 )|| (j == 0) ||( i == image->height -1 ) || (j == image->width -1 ))  
  58.                 {  
  59.                     *dst = 0;  
  60.                     //break;  
  61.                 }  
  62.                 else{  
  63.                 flag2 = true;  
  64.                 for (r = i-1 ; r<= i+1 ; r++)  
  65.                 {  
  66.                     for (s = j -1 ; s<= j+1 ; s++)  
  67.                     {  
  68.                         ptr  = (unsigned char *)(image2->imageData + r*image2->widthStep + j);  
  69.                         if(*ptr != 255){  
  70.                             flag2 =false;  
  71.                         }  
  72.                     }  
  73.                 }  
  74.                   
  75.                 if (flag2)  
  76.                 {  
  77.                     *dst = 255;  
  78.                 }  
  79.                 else {*dst = 0;}  
  80.                 }  
  81.             }  
  82.         }  
  83.         cvShowImage("image3",image3);  
  84.           
  85.   
  86.     //开操作 先腐蚀 后膨胀   
  87.         for (i = 0 ; i< image4->height; i++)  
  88.         {  
  89.             for (j = 0 ; j< image4->width ; j++)  
  90.             {  
  91.                 dst =  (unsigned char *)(image4->imageData + i*image4->widthStep + j);  
  92.                 if ((i == 0 )|| (j == 0) ||( i == image->height -1 ) || (j == image->width -1 ))  
  93.                 {  
  94.                     *dst = 0;  
  95.                     //break;  
  96.                 }  
  97.                 else{  
  98.                 flag2 = true;  
  99.                 for (r = i-1 ; r<=  i+1 ; r++)  
  100.                 {  
  101.                     for (s = j -1 ; s<= j+1 ; s++)  
  102.                     {  
  103.                         ptr  = (unsigned char *)(image3->imageData + r*image3->widthStep + s);  
  104.                         if(*ptr != 255){  
  105.                             flag2 =false;  
  106.                         }  
  107.                     }  
  108.                 }  
  109.                   
  110.                 if (flag2)  
  111.                 {  
  112.                     *dst = 255;  
  113.                 }  
  114.                 else {*dst = 0;}  
  115.                 }  
  116.             }  
  117.         }  
  118.         cvShowImage("image4",image4);  
  119.         //膨胀操作  
  120.         for (i = 0 ; i< image5->height; i++)  
  121.         {  
  122.             for (j = 0 ; j< image5->width ; j++)  
  123.             {  
  124.                 dst =  (unsigned char *)(image5->imageData + i*image5->widthStep + j);  
  125.                 if ((i == 0 )|| (j == 0) ||( i == image5->height -1 ) || (j == image5->width -1 ))  
  126.                 {  
  127.                     *dst = 0;  
  128.                     //break;  
  129.                 }  
  130.                 else{  
  131.                 flag2 = false;  
  132.                 for (r = i-1 ; r<= i+1 ; r++)  
  133.                 {  
  134.                     for (s = j -1 ; s<= j+1 ; s++)  
  135.                     {  
  136.                         ptr  = (unsigned char *)(image4->imageData + r*image4->widthStep + s);  
  137.                         if(*ptr == 255){  
  138.                             flag2 = true;  
  139.                         }  
  140.                     }  
  141.                 }  
  142.                   
  143.                 if (flag2)  
  144.                 {  
  145.                     *dst = 255;  
  146.                 }  
  147.                 else {*dst = 0;}  
  148.                 }  
  149.             }  
  150.         }  
  151.             cvShowImage("image5",image5);  
  152.   
  153.   
  154.      //作差  
  155.       cvSub(image3,image5,image6,0);  
  156.       //并运算  
  157.       for (i = 0 ; i< result->height; i++)  
  158.       {  
  159.           for (j = 0 ; j< result->width ; j++)  
  160.           {  
  161.               dst = (unsigned char *)(result->imageData + result->widthStep *i +j);  
  162.               ptr = (unsigned char *)(image6->imageData + image6->widthStep *i +j);  
  163.               if (*ptr == 255)  
  164.               {  
  165.   
  166.                   *dst = 255;  
  167.               }  
  168.           }  
  169.       }  
  170.         cvShowImage("image6",image6);  
  171.       //将腐蚀后的图像复制给当前图像image2  
  172.      image2 =  cvCloneImage(image3);  
  173.   
  174.       //循环标志判定  
  175.       flag = false;  
  176.       for (i = 0 ; i< image2->height; i++)  
  177.       {  
  178.           for (j = 0 ; j< image2->width ; j++)  
  179.           {  
  180.                
  181.               ptr = (unsigned char *)(image2->imageData + image2->widthStep *i +j);  
  182.               if (*ptr == 255)  
  183.               {  
  184.                   flag = true;  
  185.               }  
  186.           }  
  187.       }  
  188.       cvShowImage("result", result);  
  189.       cvWaitKey(40);  
  190.     }  
  191.       
  192.     cvShowImage("image2", image2);  
  193.     cvShowImage("image3", image3);  
  194.     //cvShowImage("image4", image4);  
  195.     //cvShowImage("image5", image5);  
  196.     //cvShowImage("image6", image6);  
  197.     cvShowImage("result",result);  
  198.     cvSaveImage("E:\\image\\bonegujia.bmp",result);  
  199.     cvWaitKey(0);  
  200.     cvReleaseImage(&image);  
  201.     cvDestroyAllWindows();  
  202.     return 0;  
  203. }  

原图:


提取的骨架:


DNA原图:


骨架:


形态学算法对于提取交叉的物体,会产生断裂,一般会在提取之后紧跟连接操作。




角点提取源代码:采用击中击不中变换

[cpp] view plain copy
  1. /******************************************************************** 
  2. 形态学基本操作采用二值图像 形态学方法在边界获取和形状检测中有很多应用 寻找角点 
  3. ***********************************************************************/  
  4. #include<cv.h>  
  5. #include <highgui.h>  
  6. int main(){  
  7.     IplImage * image,*image2,*image3,*image4,*result;  
  8.     image = cvLoadImage("E:\\image\\jiaodian.bmp", 0);  
  9.     cvNamedWindow("image",1);  
  10.     cvShowImage("image",image);  
  11.   
  12.   
  13.     /*击中击不中变换 寻找角点*/   
  14.     /************************************************************************/  
  15.     /*   最终结果为(79, 85 )和 (129 ,134)两个符合要求的角点  在结果图像中可以看到两个白点*/  
  16.     /************************************************************************/  
  17.     image2 = cvCreateImage(cvSize(image->width, image->height),image->depth ,1);  
  18.     image3 = cvCreateImage(cvSize(image->width, image->height),image->depth ,1);  
  19.     image4 = cvCreateImage(cvSize(image->width, image->height),image->depth ,1);  
  20.     result = cvCreateImage(cvSize(image->width, image->height),image->depth ,1);  
  21.   
  22.     int  i,j,r,s ;  
  23.     unsigned char B1[9] ={ 255,255,255,255,0,0,255,0,0};  
  24.     unsigned char B2[9] = {0,0,0,0,255,255,0,255,255 };  
  25.     int flag;  
  26. //iamge2 是对image进行求补的结果  
  27.     unsigned char *ptr, *dst,*ptr2;  
  28.     for (i = 0 ; i< image2->height; i++)  
  29.     {  
  30.         for (j = 0 ; j< image2->width; j++)  
  31.         {  
  32.             ptr = (unsigned char*)image->imageData + i * image->widthStep+ j;  
  33.             dst = (unsigned char*)image2->imageData + i*image2->widthStep + j;  
  34.             *dst = 255- (*ptr);  
  35.   
  36.         }  
  37.     }  
  38.   
  39.     //对源图像进行腐蚀  
  40.     for (i = 0 ; i< image3->height; i++)  
  41.     {  
  42.         for (j = 0 ; j< image3->width; j++)  
  43.         {  
  44.             flag = 1;  
  45.             dst = (unsigned char*)image3->imageData + i*image3->widthStep +j;  
  46.             //边界判断  
  47.             if (( i == 0) || (j == 0) || (i == image3->height-1) || (j == image3->width -1 ))  
  48.             {  
  49.                 *dst = 0;  
  50.             }  
  51.             else{  
  52.                     for (r = i -1 ; r<= i+1; r++)  
  53.                     {  
  54.                         for (s = j-1 ; s <= j +1 ; s++)  
  55.                         {  
  56.                             ptr = (unsigned char*)image->imageData + r * image->widthStep+ s;  
  57.                             if (*ptr != B1[3*(r-i+1) + s-j+1])  
  58.                             {  
  59.                             flag = 0;  
  60.                             break;  
  61.                             }  
  62.                         }  
  63.                     }  
  64.                     if (flag == 1)  
  65.                     {  
  66.                         ptr = (unsigned char*)image->imageData + i * image->widthStep+ j;  
  67.                         *dst = 255;  
  68.                     }  
  69.                     else{  
  70.                         *dst = 0;  
  71.                     }  
  72.   
  73.                 }  
  74.   
  75.         }  
  76.     }  
  77.   
  78.     //显示腐蚀结果  
  79.   
  80.     for (i = 0 ; i< image2->height; i++)  
  81.     {  
  82.         for (j = 0 ; j< image2->width; j++)  
  83.         {  
  84.             ptr = (unsigned char*)image->imageData + i * image->widthStep+ j;  
  85.             ptr2 = (unsigned char*)image3->imageData + i*image3->widthStep + j;  
  86.             if (*ptr2  == 255)  
  87.             {  
  88.                 printf("x, %d  y: %d   %d\n",j, i, *ptr2 - *ptr);  
  89.             }  
  90.               
  91.         }  
  92.     }  
  93.     //对补图像进行腐蚀  
  94.     for (i = 0 ; i< image4->height; i++)  
  95.     {  
  96.         for (j = 0 ; j< image4->width; j++)  
  97.         {  
  98.             flag = 1;  
  99.             dst = (unsigned char*)image4->imageData + i*image4->widthStep +j;  
  100.             //边界判断  
  101.             if (( i == 0) || (j == 0) || (i == image4->height-1) || (j == image4->width -1 ))  
  102.             {  
  103.                 *dst = 0;  
  104.             }  
  105.             else{  
  106.                 for (r = i -1 ; r<= i+1; r++)  
  107.                 {  
  108.                     for (s = j-1 ; s <= j +1 ; s++)  
  109.                     {  
  110.                         ptr = (unsigned char*)image2->imageData + r * image2->widthStep+ s;  
  111.                         if ((*ptr) != B2[3*(r- i+1) + s-j +1])  
  112.                         {  
  113.                             flag = 0;  
  114.                         }  
  115.                     }  
  116.                 }  
  117.                 if (flag == 1)  
  118.                 {  
  119.                     ptr = (unsigned char*)image2->imageData + i * image2->widthStep+ j;  
  120.                     *dst = 255;  
  121.                 }  
  122.                 else{  
  123.                     *dst = 0;  
  124.                 }  
  125.   
  126.             }  
  127.   
  128.         }  
  129.     }  
  130.     //显示腐蚀结果  
  131.   
  132.     for (i = 0 ; i< image4->height; i++)  
  133.     {  
  134.         for (j = 0 ; j< image4->width; j++)  
  135.         {  
  136.             ptr = (unsigned char*)image2->imageData + i * image->widthStep+ j;  
  137.             ptr2 = (unsigned char*)image4->imageData + i*image3->widthStep + j;  
  138.             if (*ptr2  == 255)  
  139.             {  
  140.                 printf("x, %d  y: %d   %d\n",j,i, *ptr2 - *ptr);  
  141.             }  
  142.   
  143.         }  
  144.     }  
  145.     //二者求交集  
  146.     for (i = 0 ; i< result->height; i++)  
  147.     {  
  148.         for (j = 0 ; j< result->width; j++)  
  149.         {  
  150.             ptr = (unsigned char *)image3->imageData + image3->widthStep * i + j;  
  151.             ptr2 = (unsigned char *)image4->imageData + image4->widthStep * i + j;  
  152.             dst = (unsigned char *)result->imageData +  result->widthStep*i + j;  
  153.             if (((*ptr) == 255) && ((*ptr2) == 255))  
  154.             {  
  155.                 *dst = 255;  
  156.                 printf("x : %d\n" , j);  
  157.                 printf("y : %d\n" , i);  
  158.   
  159.             }  
  160.             else{  
  161.                   
  162.                 *dst = 0;  
  163.             }  
  164.   
  165.         }  
  166.     }  
  167.     cvNamedWindow("image3",1);  
  168.     cvNamedWindow("image4",1);  
  169.     cvNamedWindow("result",1);  
  170.     cvShowImage("image3",image3);  
  171.     cvShowImage("image4",image4);  
  172.     cvShowImage("result",result);  
  173.     //cvSaveImage("mapleleafboard.bmp", image3);  
  174.     cvWaitKey(0);  
  175.     cvReleaseImage(&image);  
  176.     cvReleaseImage(&image2);  
  177.     cvReleaseImage(&image3);  
  178.     cvDestroyAllWindows();  
  179.     return 0 ;  
  180. }  
原图:


提取的角点:


如果要提取十字型角点只需换用十字型的模板即可

(4)       噪声滤除

对图像中的噪声进行滤除是图像预处理中不可缺少的操作。将开启和闭合运算结合起来可构成形态学噪声滤除器。

对于二值图像,噪声表现为目标周围的噪声块和目标内部的噪声孔。用结构元素B对集合A进行开启操作,就可以将目标周围的噪声块消除掉;用B对A进行闭合操作,则可以将目标内部的噪声孔消除掉。该方法中,对结构元素的选取相当重要,它应当比所有的噪声孔和噪声块都要大。

对于灰度图像,滤除噪声就是进行形态学平滑。实际中常用开启运算消除与结构元素相比尺寸较小的亮细节,而保持图像整体灰度值和大的亮区域基本不变;用闭合运算消除与结构元素相比尺寸较小的暗细节,而保持图像整体灰度值和大的暗区域基本不变。将这两种操作综合起来可达到滤除亮区和暗区中各类噪声的效果。同样的,结构元素的选取也是个重要问题。


 

 

四 选取结构元素的方法

分析表明,各种数学形态学算法的应用可分解为形态学运算和结构元素选择两个基本问题,形态学运算的规则已由定义确定,于是形态学算法的性能就取决于结构元素的选择,亦即结构元素决定着形态学算法的目的和性能。因此如何自适应地优化确定结构元素,就成为形态学领域中人们长期关注的研究热点和技术难点。目前较多采用多个结构元素对图像进行处理的方法。

 

(1)       多结构元素运算

在许多形态学应用中,往往只采用一个结构元素,这通常不能产生满意的结果。在模式识别中,如果要提取某个特定的模式,只采用一个结构元素,那么,只有与结构元素形状、大小完全相同的模式才能被提取,而与此结构元素表示的模式即使有微小差别的其他模式的信息都不能获取。

解决此问题的一个有效方法之一就是将形态学运算与集合运算结合起来,同时采用多个结构元素,分别对图像进行运算,然后将运算后的图像合并起来,即多结构元素形态学运算。

 

(2)       用遗传算法选取结构元素

遗传算法的思想来源于自然界物竞天择、优胜劣汰、适者生存的演化规律和生物进化原理,并引用随机统计理论而形成,具有高效并行全局优化搜索能力,能有效地解决机器学习中参数的复杂优化和组合优化等难题。

近年来不少国外学者已进行了这方面的探索与研究,Ehrgardt设计了形态滤波的遗传算法,用于二值图像的去噪和根据二值纹理特性消除预定目标;Huttumen利用遗传算法构造了软式形态滤波器及其参数优化的设计方法,以实现灰度图像的降噪功能。余农、李予蜀等人用遗传算法在自然景象的目标检测与提取方面进行了研究,通过自适应优化训练使结构元素具有图像目标的形态结构特征,从而赋予结构元素特定的知识,使形态滤波过程融入特有的智能,以实现对复杂变化的图像具有良好的滤波性能和稳健的适应能力。其实质是解决滤波器设计中知识获取和知识精炼的机器学习问题。

 

五 数学形态学存在的问题与进一步的研究方向

数学形态学是一门建立在集论基础之上的学科,是几何形状分析和描述的有力工具。近年来,数学形态学在数字图像处理、计算机视觉与模式识别等领域中得到了越来越广泛的应用,渐渐形成了一种新的数字图像分析方法和理论,引起了国内外相关领域研究人员的广泛关注。目前,数学形态学存在的问题及研究方向主要集中在以下几个方面:

(1)            形态运算实质上是一种二维卷积运算,当图像维数较大时,特别是用灰度形态学、软数学形态学、模糊形态学等方法时,运算速度很慢,因而不适于实时处理。

(2)            由于结构元素对形态运算的结果有决定性的作用,所以,需结合实际应用背景和期望合理选择结构元素的大小与形状。

(3)             软数学形态学中关于结构元素核心、软边界的定义,及对加权统计次数*的选择也具有较大的灵活性,应根据图像拓扑结构合理选择,没有统一的设计标准。

(4)           为达到最佳的滤波效果,需结合图像的拓扑特性选择形态开、闭运算的复合方式。

(5)            对模糊形态学,不同的模糊算子会直接影响模糊形态学的定义及其运算结果。

(6)           有待进一步将数学形态学与神经网络、模糊数学结合研究灰度图像、彩色图像的处理和分析方法。

(7)            有待进一步研究开发形态运算的光学实现及其它硬件实现方法。

(8)            有待将形态学与小波、分形等方法结合起来对现有图像处理方法进行改进,进一步推广应用。所以如何实现灰度形态学、软数学形态学、模糊软数学形态学的快速算法,如何改善形态运算的通用性,增强形态运算的适应性,并结合数学形态学的最新应用进展,将其应用到图像处理领域,丰富和发展利用数学形态学的图像处理与分析方法,成为数学形态学今后的发展方向。

 

六 结论

数学形态学对图像的处理具有直观上的简明性和数学上的严谨性,在定量描述图像的形态特征上具有独特的优势,为基于形状细节进行图像处理提供了强有力的手段。建立在集合理论基础上的数学形态学,主要通过选择相应的结构元素采用膨胀、腐蚀、开启、闭合#种基本运算的组合来处理图像。数学形态学在图像处理中的应用广泛,有许多实用的算法,但在每种算法中结构元素的选取都是一个重要的问题。


文章转自:http://blog.csdn.net/renshengrumenglibing/article/details/7177695


2014-08-14 23:23:50 lengyun_5850 阅读数 1402

数字形态学是图像处理与分析领域的重要工具之一。数学形态学可以用来解决抑制噪声、特征提取、边缘检测、图像分割、形状识别、纹理分析、图像恢复与重建、图像压缩等图像处理问题。本文将会对形态学的图像处理进行一些通俗的原理解释和Matlab代码验证。

数学形态学的语言是集合论。数学形态学中的集合表示图像中的对象。形态学的图像处理包括腐蚀,膨胀,开操作,闭操作,边界提取,孔洞填充,连通分量提取,凸壳,细化粗化,骨架,裁剪等等,其中,腐蚀和膨胀是形态学处理的原始操作,诸多形态学算法都是以这两种原始操作做为基础的。

结构元:研究一幅图像中感兴趣所用的小集合或子图像。

腐蚀和膨胀的集合表示如下:

其中,集合B是一个结构元。

腐蚀是在二值图中,使用结构元B对图像E进行移位后与操作,如果都为1,X图像的该像素则为1,否则为0。

膨胀是在二值图中,使用结构元B对图像E进行移位后与操作,如果都为0,X图像的该像素则为0,否则为1。

Matlab验证如下:

SE=strel('square',3);%创建结构元
I=imread('ex1.bmp');
figure(1);
subplot(221);imshow(I);title('原图像')
BW=imdilate(I,SE);%膨胀
%figure(2);
subplot(222);imshow(BW);title('膨胀后的图像')
BW1=imerode(I,SE);%腐蚀
%figure(3);
subplot(223);imshow(BW1);title('腐蚀后的图像')

结果如下:


由此可看出,腐蚀操作可以把小于结构元的块点去除,保留较大的块点,与此相反,膨胀操作是把细小的块点变大。由此可以用来桥接断裂的点线。

形态学开运算和闭运算

开运算和闭运算的基本操作是腐蚀和膨胀,开运算是先腐蚀后膨胀,闭运算是先膨胀后腐蚀。

点击打开链接

2014-08-20 09:30:30 littleThink 阅读数 866

微笑首先介绍两个最简单的形态学处理:膨胀(dilate)和腐蚀(erode)

它们的作用:1.去除噪声

2.分离出单个元素和连接分离的元素

3.找出强度凸起和孔洞

膨胀是使用核去遍历图像,图像在核覆盖区域中寻找最大值,以最大值覆盖核心处图像值。结果必然使图像高亮度区域增加。

腐蚀是膨胀的姊妹,但腐蚀是寻找核覆盖区域下的最小值,以最小值覆盖核心处图像值。结果必然使图像低亮度区域增加。

#include "cv.h"
#include "highgui.h"

using namespace std;
using namespace cv;

Mat src,dilateDst,erodeDst;
int dilateShape=0,erodeShape=0,dilateSize=0,erodeSize=0;

void dilateFcn(int, void*);
void erodeFcn(int, void*);

int main(int argc,char *argv[])
{
	
	src=imread("src.jpg");
	imshow("src",src);
	namedWindow("Dilate");
	namedWindow("Erode");
	
	createTrackbar("Kernel shape:(0:RECT;1:CROSS;2:ELLIPSE)","Dilate",&dilateShape,2);
	createTrackbar("Kernel size:","Dilate",&dilateSize,10,dilateFcn);

	createTrackbar("Kernel shape:(0:RECT;1:CROSS;2:ELLIPSE)","Erode",&erodeShape,2);
	createTrackbar("Kernel size:","Erode",&erodeSize,10,erodeFcn);
	
	dilateFcn(0,0);erodeFcn(0,0);
	waitKey(0);
	destroyAllWindows();
	return 0;
}

void dilateFcn(int, void*)
{
	switch(dilateShape)
	{
	case 0:
		dilateShape=MORPH_RECT;
		break;
	case 1:
		dilateShape=MORPH_CROSS;
		break;
	case 2:
		dilateShape=MORPH_ELLIPSE;
	}
	Mat dilateKernel=getStructuringElement(dilateShape,Size(2*dilateSize+1,2*dilateSize+1),Point(-1,-1));
	dilate(src,dilateDst,dilateKernel);
	imshow("Dilate",dilateDst);
}

void erodeFcn(int, void*)
{
	switch(erodeShape)
	{
	case 0:
		erodeShape=MORPH_RECT;
		break;
	case 1:
		erodeShape=MORPH_CROSS;
		break;
	case 2:
		erodeShape=MORPH_ELLIPSE;
	}
	Mat erodeKernel=getStructuringElement(erodeShape,Size(2*erodeSize+1,2*erodeSize+1),Point(-1,-1));
	erode(src,erodeDst,erodeKernel);
	imshow("Erode",erodeDst);
}
窗口TrackBar不美观

  原图:                   膨胀(核为矩形7*7)

       

   膨胀(核为十字7*7)                   膨胀(核为椭圆7*7)

       

  腐蚀(核为矩形9*9)                     腐蚀(核为十字9*9)

       

  腐蚀(核为椭圆9*9)

  

可以看到结果图像上的白点或黑点的形状与核的形状一致!

微笑使用morphologyEx()函数可以完成更多的形态学处理:

– Opening


– Closing


– Morphological Gradient(形态梯度)


– Top Hat


– Black Hat






2017-10-11 09:27:50 baidu_32173921 阅读数 341

最基本两个形态学运算----膨胀与腐蚀


膨胀与腐蚀能够实现以下作用:

   1.消除噪声

   2.分割出独立的图像元素,在图像中连接相邻的元素

   3.寻找图像中的明显的极大值区域或者极小值区域

   4.求出图像的梯度


需要注意之处: 腐蚀和膨胀都是对图像的白色部分(高亮部分)而言。膨胀是图像中的高亮部分进行膨胀,类似于领域扩张,效果图拥有比原图更大的高亮区域;腐蚀是原图的高亮部分被腐蚀,类似于领域被蚕食,效果图拥有比原图更小的高亮区域。

从数学的角度来说,膨胀和腐蚀操作就是将图像与核进行卷积,核可以是任意形状和大小的。


*膨胀(dilate)

   膨胀就是求局部最大值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐增长。

以下是膨胀操作的示意图:



*腐蚀(erode)

   腐蚀就是求局部最小值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最小值,并把这个最小值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐减少。

以下是腐蚀操作的示意图:




更加高级的形态学变换

运用膨胀和腐蚀这两个基本操作,可实现较高级的形态学变换,如 开运算,闭运算,形态学梯度,顶帽,黑帽。



*开运算(opening Operation)

  开运算其实就是先腐蚀再膨胀,数学表达式:

   dist = open(src,element) = dilate(erode(src,element))  

  开运算可以用来消除小物体,在纤细处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。


*闭运算(closing Operation)

  闭运算其实就是先膨胀再腐蚀,数学表达式:

   dist = close(src,element) = erode(dilate(src,element))  

  闭运算能够排除小型黑洞(黑色区域)。


*形态学梯度(morphological Gradient)

 形态学梯度就是膨胀图与腐蚀图之差,数学表达式:

  dist = morph-grad(src,element) = dilate(src,element) - erode(src,element)

 对二值图像进行这一操作,可以将团块的边缘突出出来,我们可以用形态梯度来保留物体的边缘轮廓


*顶帽(top Hat)

 顶帽就是原图与开运算图之差,数学表达式:

 dist = tophat(src,element) = src - open(src,element)

 因为开运算带来的结果是放大了裂痕或者局部低亮度的区域。因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小有关。

 顶帽运算往往用来分离比邻近点亮一些的斑块,在一幅图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。


*黑帽(black Hat)

 黑帽就是原图与闭运算图之差,数学表达式:

 dist = blackhat(src,element) = close(src,element)  - src

 黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作与核的大小有关。

 黑帽运算用来分离比临近点暗一点的斑块,效果图有着非常完美的轮廓

2016-11-15 09:58:22 yangleo1987 阅读数 17923

最基本两个形态学运算----膨胀与腐蚀


膨胀与腐蚀能够实现以下作用:

   1.消除噪声

   2.分割出独立的图像元素,在图像中连接相邻的元素

   3.寻找图像中的明显的极大值区域或者极小值区域

   4.求出图像的梯度


需要注意之处: 腐蚀和膨胀都是对图像的白色部分(高亮部分)而言。膨胀是图像中的高亮部分进行膨胀,类似于领域扩张,效果图拥有比原图更大的高亮区域;腐蚀是原图的高亮部分被腐蚀,类似于领域被蚕食,效果图拥有比原图更小的高亮区域。

从数学的角度来说,膨胀和腐蚀操作就是将图像与核进行卷积,核可以是任意形状和大小的。


*膨胀(dilate)

   膨胀就是求局部最大值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐增长。

以下是膨胀操作的示意图:



*腐蚀(erode)

   腐蚀就是求局部最小值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最小值,并把这个最小值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐减少。

以下是腐蚀操作的示意图:




更加高级的形态学变换

运用膨胀和腐蚀这两个基本操作,可实现较高级的形态学变换,如 开运算,闭运算,形态学梯度,顶帽,黑帽。



*开运算(opening Operation)

  开运算其实就是先腐蚀再膨胀,数学表达式:

   dist = open(src,element) = dilate(erode(src,element)) 

  开运算可以用来消除小物体,在纤细处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。


*闭运算(closing Operation)

  闭运算其实就是先膨胀再腐蚀,数学表达式:

   dist = close(src,element) = erode(dilate(src,element)) 

  运算能够排除小型黑洞(黑色区域)。


*形态学梯度(morphological Gradient)

 形态学梯度就是膨胀图与腐蚀图之差,数学表达式:

  dist = morph-grad(src,element) = dilate(src,element) - erode(src,element)

 对二值图像进行这一操作,可以将团块的边缘突出出来,我们可以用形态梯度来保留物体的边缘轮廓


*顶帽(top Hat)

 顶帽就是原图与开运算图之差,数学表达式:

 dist = tophat(src,element) = src - open(src,element)

 因为开运算带来的结果是放大了裂痕或者局部低亮度的区域。因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小有关。

 顶帽运算往往用来分离比邻近点亮一些的斑块,在一幅图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。


*黑帽(black Hat)

 黑帽就是原图与闭运算图之差,数学表达式:

 dist = blackhat(src,element) = close(src,element)  - src

 黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作与核的大小有关。

 黑帽运算用来分离比临近点暗一点的斑块,效果图有着非常完美的轮廓



以上就是形态学的常用操作方法的介绍,下一步就是实践应用了!

 


没有更多推荐了,返回首页