• 形态学图像处理()

    2016-12-31 21:38:10
    二值形态学的经典应用, 细化和像素化, 以及凸壳 灰度图像的形态学运算, 包括灰度腐蚀、灰度膨胀、灰度开和灰度闭 本章的典型案例分析 在人脸局部图像中定位嘴的中心 显微镜下图像的细菌计数 利用顶帽变换(top-...

    本文主要包括以下内容

    • 二值形态学的经典应用, 细化和像素化, 以及凸壳
    • 灰度图像的形态学运算, 包括灰度腐蚀、灰度膨胀、灰度开和灰度闭
    • 本章的典型案例分析
      • 在人脸局部图像中定位嘴的中心
      • 显微镜下图像的细菌计数
      • 利用顶帽变换(top-hat)技术解决光照不均问题

    细化算法

    “骨架”是指一副图像的骨髓部分,它描述物体的几何形状和拓扑结构,是重要的图像描绘子之一,计算骨架的过程一般称为“细化”或“骨架化”,在包括文字识别、工业零件形状识别以及印刷电路板自动检测在内的很多应用中,细化过程都发挥着关键作用。通常,对我们感兴趣的目标物体进行细化有助于突出目标的形状特点和拓扑结构并且减少冗余的信息量。



    像素化算法

    细化适用于和物体拓扑结构或形状有关的应用, 如前述的手写字符识别。 但有时我们关心的是目标对象是否存在、 它们的位置关系, 或者个数, 这时在预处理中加入像素化步骤会给后续的图像分析带来极大的方便

    理论基础
    像素化操作首先需找到二值图像中所有的连通区域, 然后用这些区域的质心作为这些连通区域的代表, 即将1个连通区域像素化为位于区域质心位置的1个像素。
    有时还可以进一步引入一个低阀值lowerThres和一个高阙值highThres来指出图像中我们感兴趣的对象连通数(连通分量中的像素数目)的大致范围, 从而只像素化图像中大小介于lowerThres和upperThres之间的连通区域, 而连通数低于lowerThres或高于upperThres的对象都将被滤除, 这就相当于使用算法的同时具有了过滤噪声的能力。如图8.29 所示。

    凸壳

    如果连接物体A内任意两点的直线段都在淫的内部,则称d是凸的。任意物体A的凸亮H是包含A的最小凸物体。
    我们总是希望像素化算法能够找到物体的质心来代表读物体,但在实际中,可能由于光照不均等原因导致图像在二值化后,物体本身形状发生缺损,像素化算法就无法找到物体真正的质心。此时可适当地进行凸壳处理,弥补凹损,算法会找到包含原始形状的最小凸多边形,如下图8.30所示.

    为确保在上述生长过程中凸壳不会大幅超出凸性所需的最小尺寸, 可以限制其生长以便凸壳不会超出初始时包含物体A的最小矩形.

    bwmorph函数
    本章的很多形态学操作都可由IPT函数bwmorph实现, 该函数的调用语法为:
    Iout = bwmorph(I,operation,n);
    operation是一个指定操作类型的字符串, 常用的合法取位如在8.2 所示

    灰度图像中的基本形态学运算

    本节就们把二值图像的形态学处理扩展到灰度图像的基本操作, 包括灰度膨胀、灰皮腐蚀、灰度开和灰皮闭。此外, 8.4.4 小节还将介绍一个灰度形态学的经典应用一一顶帽变换(top-hat), 用以解决图像的光照不均问题.

    灰度膨胀及其实现



    matlab实现
    只要以灰度图像和相应的灰度膨胀结构元素为参数调用imdilate函数即可实现灰度膨胀,平坦结构元素的创建方法与二值形态学中相同:而非平坦结构元素也可通过 strel函数以如下方式创建。
    SE = strel(NHOOD,HEIGHT);
    NHOOD 为指明结构元素定义域的矩阵, 只能由0和1 组成.
    HEIGHT 是一个与NHOOD 具有相同尺寸的矩阵, 指出了对应于NHOOD 中每个元素的高度.

    f = [0 1 2 3 4 5 4 3 2 1 0];
    figure,h_f = plot(f);
    seFlat = strel([1 1 1]);
    
    fd1 = imdilate(f,seFlat);
    hold on,h_fd1 = plot(fd1,'-ro');
    axis([1 11 0 8]);
    setHeight = strel([1 1 1],[1 1 1]);
    fd2 = imdilate(f,setHeight);
    hold on,h_fd2= plot(fd2,'-g*');
    legend('原灰度1维函数f','使用平坦结构元素膨胀','使用高度为1有结构元素膨胀元素膨胀后');

    灰度腐蚀及其实现


    与二值形态学不同的是,f(x,y)和 S(x,y)不再是只代表形状的集合,而是二维函数,它们的定义域指明了其形状, 它们的值指出了高度信息。

    f = [0 1 2 3 4 5 4 3 2 1 0];
    figure,h_f = plot(f);
    seFlat = strel([1 1 1]);
    
    fe1 = imerode(f,seFlat);
    hold on,h_fe1 = plot(fe1,'ro');
    axis([1 11 0 8]);
    seHeight = strel([1 1 1],[1 1 1]);
    
    fe2 = imerode(f,seHeight);
    hold on,h_fe2=plot(fe2,'-g*');
    legend('原灰度1维函数f','使用平坦结构元素腐蚀','使用高度为1有结构元素膨胀元素腐蚀后');

    灰度膨胀和灰度腐蚀的效果比较

    I = imread('lena.bmp');
    seHeight=strel(ones(3,3),ones(3,3));
    Idi1= imdilate(I,seHeight);
    Iero = imerode(I,seHeight);
    subplot(1,3,1),imshow(I);
    subplot(1,3,2),imshow(Idi1);
    subplot(1,3,3),imshow(Iero);


    我们看到在结构元素的值均大于零的情况下, 灰度膨胀的输出图像总体上比输入图像更亮,这是局部最大值运算作用的结果。此外原图像中一些能够包含于结构元素的暗细节〈如
    一部分帽子的槽皱和尾穗)被完全消除, 其余的大部分暗部细节也都得到了一定程度上的减少。而灰度腐蚀的作用正好相反, 输出图像比输入图像更暗, 如果输入图像中的亮部细节比结构元素小, 则亮度会得到削弱。

    灰度开、 闭运算及其实现

    与二值形态学类似,我们可以在灰度腐蚀和膨胀的基础上定义灰度开和闭运算.灰度开运算就是先灰度腐蚀后灰度膨胀,而灰度闭运算则是先灰度膨胀后灰度腐蚀,下面分别给出定义:

    这一过程: (a)为图像中的一条水平像素线:(b)和(d)分别给出了球紧贴着该像素线的下侧和上侧被动的情况:而(c)和(e)则展示了滚动过程中球的最高点形成的曲线,它们分别是开、闭运算的结果。

    Matlab实现
    使用imopen和imdilate同样可以对灰度图像进行开、闭运算, 用法与灰度腐蚀和膨胀类似, 这里不再赘述,在实际应用中, 开操作常常用于去除那些相对于结构元素s而言较小的高灰度区域〈球体滚不上去〉,而对于较大的亮区域影响不大〈球体可以滚上去〉。虽然首先进行的灰度腐蚀
    会在去除图像细节的同时使得整体灰度下降, 但随后的灰度膨胀又会增强图像的整体亮度,因此图像的整体灰度基本保持不变:而闭操作常用于去除图像中的暗细节部分, 而相对地保留高灰度部分不受影响.

    顶帽交换(top-hat)及其实现

    作为灰度形态学的重要应用之一, 这里学习一种非均匀光照问题的解决方案一一顶帽变换技术(top-hat).图像f的顶帽变换h定义为图像f与图像f的开运算之差, 可表示为:
    h = f -(f*s)

    I = imread('rice.png');
    subplot(2,4,1),imshow(I,[]);
    thresh = graythresh(I);
    Ibw = im2bw(I,thresh);
    subplot(2,4,2),imshow(Ibw,[]);
    subplot(2,4,3),surf(double(I(1:8:end,1:8:end))),zlim([0 255]),colormap;
    
    bg = imopen(I,strel('disk',15));
    subplot(2,4,4),surf(double(bg(1:8:end,1:8:end))),zlim([0 255]),colormap;
    
    Itophat = imsubtract(I,bg);
    subplot(2,4,5),imshow(Itophat);
    subplot(2,4,6),surf(double(Itophat(1:8:end,1:8:end))),zlim([0 255]);
    
    I2 = imadjust(Itophat);
    subplot(2,4,7),imshow(I2);
    thresh2 = graythresh(I2);
    Ibw2 = im2bw(I2,thresh2);
    subplot(2,4,8),imshow(Ibw2);

    展开全文
  • 学习DIP第12天 转载请标明出处:... 今天来介绍形态学中最基础也是最重要的两个操作,腐蚀和膨胀,腐蚀和膨胀基本上是所有形态学操作的基础,除此之外还有补集(即二值图全部取反的操作,0变1,1变0...

    学习DIP第12天

    转载请标明出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不满意。。。。。。。。

    开篇废话

        今天来介绍形态学中最基础也是最重要的两个操作,腐蚀和膨胀,腐蚀和膨胀基本上是所有形态学操作的基础,除此之外还有补集(即二值图全部取反的操作,0变1,1变0),和反射(将所有坐标去反)。

        之前使用过腐蚀和膨胀,仅仅是去噪,那时候连结构元(SE)的概念都没有,其实所有形态学操作,核心都是结构元(包括其形状和中心位置,中心位置可以不在结构元区域中),他的变化可以产生千奇百怪的效果,如果你能很好的设计结构元,那么你将能得到你想要的效果。

        对于写博客,我觉得坚持下来还是不错的,一是可以反思总结一下学习结果,很多收获都是写博客的时候想到的,虽然写起来浪费很多时间,包括作图之类的工作。二是可以作为资料,以后查看,查漏补缺。三是与别人分享知识,如果有问题,可以有人及时指正,良师益友。

     

    应用

     内容迁移至 

    http://www.face2ai.com/DIP-3-2-二值图像-形态学处理2-腐蚀和膨胀/

     

     http://www.tony4ai.com/DIP-3-2-二值图像-形态学处理2-腐蚀和膨胀/

    展开全文
  • 数学形态学(Mathematical morphology)是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:腐蚀和膨胀、开运算和闭运算、骨架抽取、极限腐蚀、击中击不中变换、...

    个人博客:http://www.chenjianqu.com/

    原文链接:http://www.chenjianqu.com/show-10.html

    数学形态学

    数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:腐蚀和膨胀、开运算和闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换等。

    数学形态学操作可以分为二值形态学和灰度形态学,灰度形态学由二值形态学扩展而来。数学形态学有2个基本的运算,即腐蚀和膨胀,而腐蚀和膨胀通过结合又形成了开运算和闭运算。 开运算就是先腐蚀再膨胀,闭运算就是先膨胀再腐蚀。本文介绍二值形态学变换。

     

    二值形态学变换

    二值形态学变换就是对二值图像进行数学形态学操作。常用操作有腐蚀、膨胀、开运算、闭运算等。腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。 膨胀就是图像中的高亮部分进行膨胀,“邻域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中高亮部分被腐蚀,“邻域被蚕食”,效果图拥有比原图更小的高亮区域。

     

    腐蚀

    腐蚀操作会收缩(细化)图像中物体的轮廓,可以用来断开(分离)物体间的连接,消除离散点,代价是导致物体的面积比原来的面积要小。。

    腐蚀的数学表达式为:

    1.png

    该式子表示用结构B腐蚀A,需要在B中定义一个参考点。B像在A上面移动(如图像卷积操作一般)。

    1. 用结构元素,扫描图像的每一个像素

    2. 用结构元素与其覆盖的二值图像做“与”操作

    3. 如果都为1,结果图像的该像素为1。否则为0

    也就是查找被处理图像中能不能找到和结构元素相同的矩阵。如果存在那么中心点所对应的点就为1,否则为0.下图为腐蚀操作的示意图:

    2.png

     

    代码实现:

    Mat ErosionTransform(Mat &src,int kernelSize)
    {
        Mat dst(src.rows, src.cols, src.type(), Scalar(0));
        Mat kernel = Mat::ones(kernelSize, kernelSize, CV_8UC1);
        if (src.channels() == 1) {
            int rowsSub = int(kernel.rows / 2);
            int colsSub = int(kernel.cols / 2);
            for (int i = rowsSub; i < src.rows-rowsSub; i++) {
                for (int j = colsSub; j < src.cols-colsSub; j++)
                {
                    int flag = 0;
                    for (int ki = 0; ki < kernel.rows; ki++)
                    {
                        for (int kj = 0; kj < kernel.cols; kj++)
                        {
                                int i_ = i+ki - rowsSub;
                                int j_ = j+kj - colsSub;
                                if (src.at<uchar>(i_, j_) == 0 && kernel.at<uchar>(ki, kj) >0)
                                    flag = 1;
                        }
                    }
                    if(!flag)
                        dst.at<uchar>(i, j) = 255;
                    else
                        dst.at<uchar>(i, j) = 0;
                }
            }
        }
        return dst;
    }

    说明: 

    当输入的核是一个3x3的全1矩阵时,运行效果图如下:

    3.png

     

    膨胀

    膨胀操作会扩大(粗化)图像中物体的轮廓,可以用来弥补(填充)物体间的孔洞,强化离散点,代价是导致物体的面积比原来的面积要大。

    膨胀的公式如下:

    4.png

    膨胀是和腐蚀运算相反的操作,膨胀的步骤如下:

    1. 用结构元素,扫描图像的每一个像素

    2. 用结构元素与其覆盖的二值图像做“与”操作

    3. 如果都为0,结果图像的该像素为0。否则为1

    也就是在结构元素覆盖范围下,只要有一个像素符和结构元素像素相同,那么中心点对应点就为1,否则为0.

    5.png

    代码实现如下:

    Mat DialateTransform(Mat &src, int kernelSize)
    {
        Mat dst(src.rows, src.cols, src.type(), Scalar(0));
        Mat kernel = Mat::ones(kernelSize, kernelSize, CV_8UC1);
        if (src.channels() == 1) {
            int rowsSub = int(kernel.rows / 2);
            int colsSub = int(kernel.cols / 2);
            for (int i = rowsSub; i < src.rows - rowsSub; i++) {
                for (int j = colsSub; j < src.cols - colsSub; j++)
                {
                    int flag = 0;
                    for (int ki = 0; ki < kernel.rows; ki++)
                    {
                        for (int kj = 0; kj < kernel.cols; kj++)
                        {
                            int i_ = i + ki - rowsSub;
                            int j_ = j + kj - colsSub;
                            if (src.at<uchar>(i_, j_) >0 && kernel.at<uchar>(ki, kj) >0)
                                flag = 1;
                        }
                    }
                    if (!flag)
                        dst.at<uchar>(i, j) =0;
                    else
                        dst.at<uchar>(i, j) = 255;
                }
            }
        }
        return dst;
    }

    当kernelSize=3时,运行效果图如下:

    6.png

     

     

     

    闭运算

    闭运算是使用同一结构元对图像进行先膨胀后腐蚀的操作,可以用来弥合较窄的间断和细长的沟壑,消除物体间小的孔洞,填补轮廓线中的断裂。

    闭运算=先进行膨胀运算,在进行腐蚀运算,其运行示意图如下,

    7.png

     

    代码实现:

    Mat ClosingTransform(Mat &src, int kernelSize)
    {
        Mat dialateImg = DialateTransform(src, kernelSize);
        return ErosionTransform(dialateImg, kernelSize);
    }

    8.png

     

    开运算

    开运算是使用同一结构元对图像进行先腐蚀后膨胀的操作,可以用来平滑物体的轮廓,断开物体间较窄的连接,消除物体边沿尖锐的突出部分。

    开运算 = 先腐蚀运算,再膨胀运算.示意图如下:

    9.png

    代码实现:

    Mat OpeningTransform(Mat &src, int kernelSize)
    {
        Mat erosionImg = ErosionTransform(src, kernelSize);
        return DialateTransform(erosionImg, kernelSize);
    }
    
    kernelSize=5

    10.png

     

     

    参考文献

    [1] 百度百科.数学形态学.https://baike.baidu.com/item/%E6%95%B0%E5%AD%A6%E5%BD%A2%E6%80%81%E5%AD%A6/2594174.

    [2]CSDN博客:chaolei_9527. 数学形态学运算——腐蚀、膨胀、开运算、闭运算.https://blog.csdn.net/chaolei3/article/details/79618602.2018-03-20

    [3]CSDN博客:青雲-吾道乐途. 膨胀与腐蚀的彻底击破.https://blog.csdn.net/qq_37059483/article/details/77878829.2017-09-07

    [4]CSDN博客:HanShanBuLeng. 形态学应用——图像开运算与闭运算.https://blog.csdn.net/hanshanbuleng/article/details/80657148.2018-06-11

     

    展开全文
  • 二值图像形态学处理

    2009-10-19 16:09:00
    二值图像是一种简单的图像格式,它...二值图像处理运算是从数学形态学下的集合论方法发展起来的基本运算很简单,但是却可以产生复杂的效果。常用的二值图像处理操作有许多方法,如腐蚀、膨胀、细化、开运算和闭运算等

    二值图像是一种简单的图像格式,它只有两个灰度级,即"0"表示黑色的像素点,"255"表示白色的像素点。二值图像处理在图像处理领域占据很重要的位置,在具体的图像处理应用系统中,往往需要对于获得的二值图像进行处理,以有利于后期的识别工作。二值图像处理运算是从数学形态学下的集合论方法发展起来的基本运算很简单,但是却可以产生复杂的效果。常用的二值图像处理操作有许多方法,如腐蚀、膨胀、细化、开运算和闭运算等等。

    一、腐蚀和膨胀

    形态学是一门新兴科学,它的用途主要是获取物体拓扑和结果信息,它通过物体和结构元素相互作用的某些运算,得到物体更本质的形态。它在图像处理中的应用主要是:

    1.利用形态学的基本运算,对图像进行观察和处理,从而达到改善图像质量的目的;

    2.描述和定义图像的各种几何参数和特征,如面积,周长,连通度,颗粒度,骨架和方向性。

    二值图像基本的形态学运算是腐蚀和膨胀,

    简单的腐蚀是消除物体的所有边界点的一种过程,其结果是使剩下的物体沿其周边比原物体小一个像素的面积。如果物体是圆的,它的直径在每次腐蚀后将减少两个像素,如果物体在某一点处任意方向上连通的像素小于三个,那么该物体经过一次腐蚀后将在该点处分裂为二个物体。

    简单的膨胀运算是将与某物体接触的所有背景点合并到该物体中的过程。过程的结果是使物体的面积增大了相应数量的点,如果物体是圆的,它的直径在每次膨胀后将增大两个像素。如果两个物体在某一点的任意方向相隔少于三个像素,它们将在该点连通起来。

    下面给出具体的实现腐蚀和膨胀的函数代码:

     

    // 二值图像腐蚀操作函数

    BOOL ImageErosion(BYTE  *pData, int Width, int Height)

    {

          // pData为图像数据的指针,WidthHeight为图像的宽和高;

          BYTE *  pData1;

          int m, n, i, j, sum, k, sum1;

          BOOL bErosion;

          if (pData  ==  NULL)

          {

               AfxMessageBox("图像数据为空,请读取图像数据");

               return FALSE;

          }

          // 申请空间,pData1存放处理后的数据;

          pData1 = (BYTE * )new char[WIDTHBYTES(Width  *  8)  *  Height];

          if (pData1 == NULL)

          {

               AfxMessageBox("图像缓冲数据区申请失败,请重新申请图像数据缓冲区");

               return FALSE;

          }

          memcpy(pData1,pData,WIDTHBYTES(Width * 8) * Height);

          for (i = 10;i < Height - 10;i++)

               for (j = 32;j < Width - 32;j++)

               {

                     bErosion = FALSE;

                     sum =  *(pData + WIDTHBYTES(Width * 8) * i + j);

                     // 当前像素为黑色

                     if(sum == 0)

                     {

                          for (m =  -1;m < 2;m++)

                          {

                                for (n =  -1;n < 2;n++)

                                {

                                      // 周围只要有一个像素为白色,则把当前像素设置为白色

                                      sum1 =  *(pData+WIDTHBYTES(Width * 8) *(i + m) + j + n);

                                      if(sum1 == 255)

                                      {

                                           *(pData1 + WIDTHBYTES(Width * 8) * i + j)=255;

                                           bErosion = TRUE;

                                           break;

                                      }

                                }

                                if(bErosion)

                                {

                                      bErosion = FALSE;

                                      break;

                                }

                          }

                     }

               }

               memcpy(pData,pData1,WIDTHBYTES(Width * 8) * Height);

               return TRUE;

    }

     

    // 二值图像的膨胀操作

    BOOL ImageDilation(BYTE  *pData, int Width, int Height)

    {

          BYTE *  pData1;

          int m,n,i,j,sum,k,sum1;

          BOOL bDilation;

          if(pData  == NULL)

          {

               AfxMessageBox("图像数据为空,请读取图像数据");

               return FALSE;

          }

          // 申请空间,pData1存放处理后的数据;

          pData1 = (BYTE * )new char[WIDTHBYTES(Width * 8) * Height];

          if(pData1 == NULL)

          {

               AfxMessageBox("图像缓冲数据区申请失败,请重新申请图像数据缓冲区");

               return FALSE ;

          }

          memcpy(pData1,pData,WIDTHBYTES(Width * 8) * Height);

          for (i = 10;i < Height - 10;i++)

               for (j = 32;j < Width - 32;j++)

               {

                     bDilation = FALSE;

                     sum =  *(pData+WIDTHBYTES(Width * 8) * i + j);

                     // 当前像素为白色

                     if(sum == 255)

                     {

                          for (m =  - 1;m < 2;m++)

                          {

                                for (n =  - 1;n < 2;n++)

                                {

                                      // 周围像素只要有一个像素为黑色,则把当前像素设置为黑色

                                      sum1 =  *(pData+WIDTHBYTES(Width * 8) *(i + m) + j + n);

                                      if(sum1 == 0)

                                      {

                                           *(pData1 + WIDTHBYTES(Width * 8) * i+j) = 0;

                                           bDilation = TRUE;

                                           break;

                                      }

                                }

                                if(bDilation)

                                {

                                      bDilation = FALSE;

                                      break;

                                }

                          }

                     }

               }

               memcpy(pData, pData1, WIDTHBYTES(Width * 8) * Height);

               return TRUE;

    }

    从上面的说明可以看出,腐蚀可以消除图像中小的噪声区域,膨胀可以填补物体中的空洞。对一个图像先进行腐蚀运算然后再膨胀的操作过程称为开运算,它可以消除细小的物体、在纤细点处分离物体、平滑较大物体的边界时不明显的改变其面积。如果对一个图像先膨胀然后再收缩,我们称之为闭运算,它具有填充物体内细小的空洞、连接邻近物体、在不明显改变物体面积的情况下平滑其边界的作用。通常情况下,当有噪声的图像用阈值二值化后,所得到的边界是很不平滑的,物体区域具有一些错判的孔洞,背景区域散布着一些小的噪声物体,连续的开和闭运算可以显著的改善这种情况,这时候需要在连接几次腐蚀迭代之后,再加上相同次数的膨胀,才可以产生所期望的效果。

    展开全文
  • 形态学图像处理中的应用形态学图像处理中的应用 腐蚀和膨胀 理论基础 形态学操作 膨胀 腐蚀 代码 代码说明 结果腐蚀和膨胀理论基础形态学操作 简而言之:一系列操作基于形状来操作图像,形态学操作通过在图像上...

    形态学在图像处理中的应用


    腐蚀和膨胀

    理论基础

    形态学操作

    • 简而言之:一系列操作基于形状来操作图像,形态学操作通过在图像上应用结构元素来产生输出图像。
    • 最基础的形态学操作包括腐蚀和扩张。它包含广泛的应用:
      • 移除噪声
      • 孤立一些单独的元素和聚合一些分散的元素
      • 找到图像中的局部块状或者孔
    • 我们将使用下面的图像简要的介绍膨胀和腐蚀

      这里写图片描述

    膨胀

    • 这一操作包含使用卷积核B对图片A进行卷积运算,这个卷积核可以有任意的形状和大小,通常是一个方形或者圆形。
    • 卷积核B通常有个锚点,通常位于卷积核的中央位置。
    • 随着卷积核扫描这个图像,我们计算叠加区域的最大像素值,并将锚点的位置用最大值替换。这样你可以推断最大化操作导致图片中亮的区域增长(所以这里面叫做膨胀)。举个例子,应用膨胀我们可以得到:

      这里写图片描述

      我们看到背景亮的区域膨胀变大。
      为了掌握这一思想,避免可能的混淆,我们在另外一个例子中反转了原来的图像,现在白色代表原先的字。我们使用3*3矩形元素的元素进行膨胀操作两次。


      左侧是反转之后的图像-右侧是膨胀之后的图像

    膨胀使得对象的白色区域变大。

    腐蚀

    1. 腐蚀与膨胀类似。它是计算卷积核里面的最小元素。
    2. 随着卷积核B扫描图片,它会计算B叠加区域的最小像素值,并使用这个像素值替换锚点的值。
    3. 与膨胀相似,对原始的图像应用腐蚀操作。你可以看到背景亮的区域变小,而黑的区域变得很大。



      同样对原始图像进行反转,进行腐蚀操作,得到:


      左侧为原始图像反转的图像-右侧为腐蚀的结果

      腐蚀使得对象白色变小。

    代码

    示例代码如下,你可以从这里下载代码

    #include "opencv2/imgproc.hpp"
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/highgui.hpp"
    using namespace cv;
    Mat src, erosion_dst, dilation_dst;
    int erosion_elem = 0;
    int erosion_size = 0;
    int dilation_elem = 0;
    int dilation_size = 0;
    int const max_elem = 2;
    int const max_kernel_size = 21;
    void Erosion( int, void* );
    void Dilation( int, void* );
    int main( int, char** argv )
    {
      src = imread( argv[1], IMREAD_COLOR );
      if( src.empty() )
        { return -1; }
      namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
      namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
      moveWindow( "Dilation Demo", src.cols, 0 );
      createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
              &erosion_elem, max_elem,
              Erosion );
      createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
              &erosion_size, max_kernel_size,
              Erosion );
      createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
              &dilation_elem, max_elem,
              Dilation );
      createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
              &dilation_size, max_kernel_size,
              Dilation );
      Erosion( 0, 0 );
      Dilation( 0, 0 );
      waitKey(0);
      return 0;
    }
    void Erosion( int, void* )
    {
      int erosion_type = 0;
      if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
      else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
      else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
      Mat element = getStructuringElement( erosion_type,
                           Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                           Point( erosion_size, erosion_size ) );
      erode( src, erosion_dst, element );
      imshow( "Erosion Demo", erosion_dst );
    }
    void Dilation( int, void* )
    {
      int dilation_type = 0;
      if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
      else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
      else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
      Mat element = getStructuringElement( dilation_type,
                           Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                           Point( dilation_size, dilation_size ) );
      dilate( src, dilation_dst, element );
      imshow( "Dilation Demo", dilation_dst );
    }

    代码说明

    1. 所有的这些东西如果你不清楚,可以参照前面的介绍。我们先看一看程序的结构:
      • 加载图像(可以是BGR也可以是灰度图像)
      • 创建两个窗口(一个是膨胀输出,一个是腐蚀输出)
      • 对于每个操作常见一组滑动条
        • 第一个元素返回元素类型erosion_elem 或者dilation_elem
        • 第二个元素返回卷积核的大小
      • 当我们移动滑动条的时候,用户的函数Erosion 和Dilation 会被调用,它会根据滑动条上的值更新输出图像
    2. 腐蚀
    
    void Erosion( int, void* )
    {
      int erosion_type = 0;
      if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
      else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
      else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
      Mat element = getStructuringElement( erosion_type,
                           Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                           Point( erosion_size, erosion_size ) );
      erode( src, erosion_dst, element );
      imshow( "Erosion Demo", erosion_dst );
    }
    • 该函数使用cv::erode进行腐蚀操作,它接收三个参数:

      • src:源图像
      • erosion_dst:目标输出图像
      • element:我们将使用的卷积核。如果不指定,将使用3*3的矩阵。我们可以指定形状。为了指定形状,我们可以使用cv::getStructuringElement 函数:
        
         Mat element = getStructuringElement( erosion_type,
                           Size( 2*erosion_size + 1, 2*erosion_size+1 ),
                           Point( erosion_size, erosion_size ) );

      我们可以为卷积核选择以下形状:
      - 矩形框:MORPH_RECT
      - 十字框:MORPH_CROSS
      - 椭圆框:MORPH_ELLIPSE
      然后我们只需指定核的大小和锚点。如果没有指定,假定在中心。

    • 指定这些值之后,我们就可以进行图像的腐蚀操作了。

    3.膨胀
    膨胀的算法与腐蚀的算法类似。

    void Dilation( int, void* )
    {
      int dilation_type = 0;
      if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
      else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
      else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
      Mat element = getStructuringElement( dilation_type,
                           Size( 2*dilation_size + 1, 2*dilation_size+1 ),
                           Point( dilation_size, dilation_size ) );
      dilate( src, dilation_dst, element );
      imshow( "Dilation Demo", dilation_dst );
    }

    结果

    编译代码,运行时使用以下图片:



    调整滑动条的参数值,得到类似下面结果:


    更多形态学转换

    理论基础

    在上面我们已经了解到形态学两种基本操作:

    • 膨胀
    • 腐蚀
      在这两种基础操作的基础之上,我们探究图像的更为复杂的转换。这里我们讨论OpenCV提供的5中操作。

    开操作

    • 它通过先进性腐蚀操作,再进行膨胀操作得到
    d s t = o p e n ( s r c , e l e m e n t ) = d i l a t e ( e r o d e ( s r c , e l e m e n t ) )

    • 在移除小的对象时候很有用(假设物品是亮色,前景色是黑色)
    • 如下所示。左侧是原始图像右侧是应用开操作之后的图像。我们可以看到左侧图像的小的空间消失



      为了更清晰,对原先对象进行反转,然后再进行开操作,结果如下:


      左侧为原始图像反转-右侧为开操作之后的图像

    闭操作

    • 比操作是先进行膨胀然后进行腐蚀操作
      dst = close( src, element ) = erode( dilate( src, element ) )

    • 有利于移除小的洞(黑色区域)




      便于说明问题,对反转的图像进行闭操作


      左侧反转图像-右侧为进行闭操作之后的图像

    梯度操作

    • 是膨胀操作与腐蚀操作的差
      dst=morphgrad(src,element)=dilate(src,element)erode(src,element)
    • 对于寻找对象的轮廓很有用,如下:


    顶帽操作

    • 是原图像与开操作对象的差
      dst=tophat(src,element)=srcopen(src,element)


    黑帽操作

    • 是闭操作与原始图像的差值
      dst=blackhat(src,element)=close(src,element)src



      示例代码

      教程代码如下,你可以从这里下载

    #include "opencv2/imgproc.hpp"
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/highgui.hpp"
    using namespace cv;
    Mat src, dst;
    int morph_elem = 0;
    int morph_size = 0;
    int morph_operator = 0;
    int const max_operator = 4;
    int const max_elem = 2;
    int const max_kernel_size = 21;
    const char* window_name = "Morphology Transformations Demo";
    void Morphology_Operations( int, void* );
    int main( int, char** argv )
    {
      src = imread( argv[1], IMREAD_COLOR ); // Load an image
      if( src.empty() )
        { return -1; }
      namedWindow( window_name, WINDOW_AUTOSIZE ); // Create window
      createTrackbar("Operator:\n 0: Opening - 1: Closing  \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
      createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
                      &morph_elem, max_elem,
                      Morphology_Operations );
      createTrackbar( "Kernel size:\n 2n +1", window_name,
                      &morph_size, max_kernel_size,
                      Morphology_Operations );
      Morphology_Operations( 0, 0 );
      waitKey(0);
      return 0;
    }
    void Morphology_Operations( int, void* )
    {
      // Since MORPH_X : 2,3,4,5 and 6
      int operation = morph_operator + 2;
      Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
      morphologyEx( src, dst, operation, element );
      imshow( window_name, dst );
    }

    代码阐述

    1. 我们来看一下程序的结构:

      • 加载图像
      • 创建一个窗口展示形态学操作结果
      • 创建三个滑动按钮来输入参数

        • 第一个滑动按钮返回形态学操作类型,使用morph_operator createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
        • 第二个滑动条参数返回morph_elem,它表示卷积核的类型createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
          &morph_elem, max_elem,
          Morphology_Operations );
        • 最后一个参数表示morph_size表示卷积核的大小createTrackbar( "Kernel size:\n 2n +1", window_name,
          &morph_size, max_kernel_size,
          Morphology_Operations );
      • 每次你移动滑动条的时候,Morphology_Operations 函数会被调用,使得新的形态学操作有效,它会根据当前滑动按钮的值输出图像void Morphology_Operations( int, void* )
        {
        // Since MORPH_X : 2,3,4,5 and 6
        int operation = morph_operator + 2;
        Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
        morphologyEx( src, dst, operation, element );
        imshow( window_name, dst );
        }

        我们可以看到执行形态学转换的例子的关键函数是cv::morphologyEx,在这里我们指定了四个参数(剩余的使用默认值):

        • src,源输入图像
        • dst,输出文件
        • operation,执行的形态学转换。注意我们有5个可选项:
          • 开操作: MORPH_OPEN : 2
          • 闭操作: MORPH_CLOSE: 3
          • 梯度操作: MORPH_GRADIENT: 4
          • 顶帽操作: MORPH_TOPHAT: 5
          • 黑帽操作: MORPH_BLACKHAT: 6

        这里的值从2到6,这就是前面函数中加2的原因。
        int operation = morph_operator + 2;

        • element,卷积核,我们使用cv::getStructuringElement来定义元素结构

    结果

    • 在编译完代码之后,我们使用以下图片作为输入参数:



      • 这里我们显示了两个窗口截图第一幅图展示了使用十字形卷积核进行开操作的输出结果,第二幅展示了使用椭圆形卷积核进行黑帽操作得出的结果。


    利用形态学操作抽取水平和竖直线

    理论

    形态学操作

    形态学是一系列的图像处理操作,这些图像处理基于预先定义好的结构核。输出图像中的每个像素的值由输入图像的响应像素和周边像素决定。通过选择核的尺寸和大小,你可以构造对于输入图像中特定形状敏感的形态学操作。
    形态学中,最基础的两个操作是膨胀和腐蚀。膨胀向图像中的对象周边添加像素,腐蚀恰恰相反,添加或者移除 的多少依赖于构造的核的形状和大小,一般来说,这两项操作遵循以下规则:

    • 膨胀:输出的像素值是落在核内的最大值。例如一个二值图像,如果有一个落在核内的像素值是1,那么相应的输出图像的像素值为1.


      二值图像上的膨胀

      灰度图像上的膨胀
    • 腐蚀:腐蚀与膨胀恰恰相反,取最小值


      二值图像的腐蚀

      灰度图像的腐蚀
    • 构造核
      通过以上可以看出,一般的形态学操作都使用构造的核来探测输出的图像,它是最重要的。这个核通常只包含0和1两个元素,可以拥有任意的形状和大小。通常来说,要比输入的 图像小的多,值为1的元素定义邻居。核的中心元素,也叫原始元素决定要处理的元素。
      例如下面是一个7*7大小的菱形核。


      菱形核和它的起始点

      构造的核元素可以有许多形状,例如线形,菱形,周期性线性,磁盘形,圆形等。通常形状和大小和输入图像中要处理的对象类似。例如,要发现图像中的线性,需要构造出线性的核,你在后面将会看到。

    示例代码

    示例代码如下,你可以在这里下载

    #include <iostream>
    #include <opencv2/opencv.hpp>
    using namespace std;
    using namespace cv;
    int main(int, char** argv)
    {
        // Load the image
        Mat src = imread(argv[1]);
        // Check if image is loaded fine
        if(!src.data)
            cerr << "Problem loading image!!!" << endl;
        // Show source image
        imshow("src", src);
        // Transform source image to gray if it is not
        Mat gray;
        if (src.channels() == 3)
        {
            cvtColor(src, gray, CV_BGR2GRAY);
        }
        else
        {
            gray = src;
        }
        // Show gray image
        imshow("gray", gray);
        // Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
        Mat bw;
        adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
        // Show binary image
        imshow("binary", bw);
        // Create the images that will use to extract the horizontal and vertical lines
        Mat horizontal = bw.clone();
        Mat vertical = bw.clone();
        // Specify size on horizontal axis
        int horizontalsize = horizontal.cols / 30;
        // Create structure element for extracting horizontal lines through morphology operations
        Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));
        // Apply morphology operations
        erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
        dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
        // Show extracted horizontal lines
        imshow("horizontal", horizontal);
        // Specify size on vertical axis
        int verticalsize = vertical.rows / 30;
        // Create structure element for extracting vertical lines through morphology operations
        Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));
        // Apply morphology operations
        erode(vertical, vertical, verticalStructure, Point(-1, -1));
        dilate(vertical, vertical, verticalStructure, Point(-1, -1));
        // Show extracted vertical lines
        imshow("vertical", vertical);
        // Inverse vertical image
        bitwise_not(vertical, vertical);
        imshow("vertical_bit", vertical);
        // Extract edges and smooth image according to the logic
        // 1. extract edges
        // 2. dilate(edges)
        // 3. src.copyTo(smooth)
        // 4. blur smooth img
        // 5. smooth.copyTo(src, edges)
        // Step 1
        Mat edges;
        adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
        imshow("edges", edges);
        // Step 2
        Mat kernel = Mat::ones(2, 2, CV_8UC1);
        dilate(edges, edges, kernel);
        imshow("dilate", edges);
        // Step 3
        Mat smooth;
        vertical.copyTo(smooth);
        // Step 4
        blur(smooth, smooth, Size(2, 2));
        // Step 5
        smooth.copyTo(vertical, edges);
        // Show final result
        imshow("smooth", vertical);
        waitKey(0);
        return 0;
    }

    阐述和结果

    1. 载入原始图像,并检查它是否加载成功,紧接着进行展示:
     // Load the image
        Mat src = imread(argv[1]);
        // Check if image is loaded fine
        if(!src.data)
            cerr << "Problem loading image!!!" << endl;
        // Show source image
        imshow("src", src);
    




    2. 如果图像没有转换,将图像转为灰度图像

     // Transform source image to gray if it is not
        Mat gray;
        if (src.channels() == 3)
        {
            cvtColor(src, gray, CV_BGR2GRAY);
        }
        else
        {
            gray = src;
        }
        // Show gray image
        imshow("gray", gray);




    3. 之后将灰度图像转为二值图像。注意到符号~表示取反:

     // Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
        Mat bw;
        adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
        // Show binary image
        imshow("binary", bw);




    4. 现在我们已经准备好了提取水平线和竖直线,同时紧接着会将音符从音符本中提取出来,但是首先初始化输出的图像
    // Create the images that will use to extract the horizontal and vertical lines
    Mat horizontal = bw.clone();
    Mat vertical = bw.clone();

    5. 前面已经介绍为了提取想要的对象,我们必须构造相应的核,为了提取水平线我们构造下面形状的核:



    在源代码中通过以下代码段展示:

      // Specify size on horizontal axis
        int horizontalsize = horizontal.cols / 30;
        // Create structure element for extracting horizontal lines through morphology operations
        Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));
        // Apply morphology operations
        erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
        dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
        // Show extracted horizontal lines
        imshow("horizontal", horizontal);
    




    6. 竖直线处理类似:


      // Specify size on vertical axis
        int verticalsize = vertical.rows / 30;
        // Create structure element for extracting vertical lines through morphology operations
        Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));
        // Apply morphology operations
        erode(vertical, vertical, verticalStructure, Point(-1, -1));
        dilate(vertical, vertical, verticalStructure, Point(-1, -1));
        // Show extracted vertical lines
        imshow("vertical", vertical);




    7. 如你所看到的那样,音符的边缘很粗糙。你需要细化边缘来获得平滑的结果:

       // Inverse vertical image
        bitwise_not(vertical, vertical);
        imshow("vertical_bit", vertical);
        // Extract edges and smooth image according to the logic
        // 1. extract edges
        // 2. dilate(edges)
        // 3. src.copyTo(smooth)
        // 4. blur smooth img
        // 5. smooth.copyTo(src, edges)
        // Step 1
        Mat edges;
        adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
        imshow("edges", edges);
        // Step 2
        Mat kernel = Mat::ones(2, 2, CV_8UC1);
        dilate(edges, edges, kernel);
        imshow("dilate", edges);
        // Step 3
        Mat smooth;
        vertical.copyTo(smooth);
        // Step 4
        blur(smooth, smooth, Size(2, 2));
        // Step 5
        smooth.copyTo(vertical, edges);
        // Show final result
        imshow("smooth", vertical);



    展开全文
  • 形态学图像处理

    2016-12-31 16:17:01
    形态学,即数学形态学(mathematical Morphology),是图像处理中应用最为广泛的技术之一,主要用于从图像中提取表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质〈最具区分能力-...
  • 学习DIP第11天 转载请标明出处:...。。。。。。。... 数学形态学(Mathematical morphology)是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括...
  •  在弄图像处理的时候,我们都会经常接触到“值...要了解值化,我们就要认识二值形态学,因为值化是它其中的一部分内容。二值形态学里的涉及的基本运算及操作多而且偏复杂,需要有一定的耐心才能很好消化吸收...
  •  收缩和膨胀是数学形态学最基本的变换,数学形态学的应用几乎覆盖了图像处理的所有领域,给出利用数学形态学对二值图像处理的一些运算。  膨胀就是把连接成分的边界扩大一层的处理。而收缩则是把
  • 文章目录一瞥二值图像形态学(binary morphology)膨胀(Dilation)膨胀属性properties of dilation举个栗子腐蚀(Erosion)腐蚀的属性(properties of erosion)举个栗子膨胀和腐蚀的对偶原理(duality)开运算和闭...
  • [算法说明]:二值图像腐蚀操作属于图像形态学的范畴,形态学运算是只针对二值图像进行,并依据数学形态学(Mathermatical Morphogy)集合论方法发展起来的数字图像处理方法,它主要包括: 腐蚀, 膨胀, 开, 闭...
  • Matlab形态学图像处理二值图像分割 标记连通区域和重心位置 删除连通区域 Matlab中可以使用graythresh(Img)函数设置二值化的阈值,再用im2bw转化为二值图像。在Matlab中,可以使用bwlabel()和bwlabeln()函数来...
  • 数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换...
  • 形态学操作内容简介 内容简介   本篇博客介绍的形态学图像处理操作。
  • 数字图像处理第九章数字图像处理---形态学图像处理(一)预备知识1.1 预备知识1.1.1 集合理论中的基本概念1.2 二值图像、集合及逻辑算子()膨胀和腐蚀2.1 膨胀2.2 结构元的分解2.3 strel函数2.4 腐蚀(三) 膨胀...
  • 数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换...
  • 二值形态学 1. 结构元素的构造与分解 2.腐蚀与膨胀 3.开操作与闭操作 4.形态学滤波器 5.击中或击不中变换 6.值图像的形态学变换 7.形态学操作 灰度形态学 1.膨胀与腐蚀 2.开操作与闭操作 3.形态学图像平滑 4.顶...
  • 了解形态操作的应用 、原理  收缩和膨胀是数学形态学最基本的变换,数学形态学的应用几乎覆盖了图像处理的所有领域,给出利用数学形态学对二值图像处理的一些运 算。  膨胀就是把连接成分...
1 2 3 4 5 ... 20
收藏数 12,284
精华内容 4,913