OpenCV2 && Qt4 chapter5: 形态学图像处理

形态学图像处理的应用可以简化图像数据,保持它们基本的形状特性,并除去不相干的结构。

一。形态学图像处理的基本运算有4个:膨胀、腐蚀、开操作和闭操作。

结构元素大小的影响:
结构元素大的话,对于前景来说,被腐蚀掉的像素点会有好多,留下来的较少。    膨胀的像素点也会多。。。。  嗯嗯。大概这么个理解法。嘻嘻。

图像的腐蚀和膨胀:(二值图像,和灰度图像,调用的都是同一个函数,理解以二值图像来理解呗。)
腐蚀作用: 去噪音。 或者断开两个区域。
膨胀作用: 可以连通两个区域。

腐蚀膨胀的具体实现:(那个句号,代表了原点。)

腐蚀算法:

简单说,首先构造一个结构元素,结构元素的原点定位在待处理的目标像素上,当结构元素完全被包容在目标区域中,则原点所对应的目标像素点被保留;否则该点被腐蚀掉——成为新的背景点。所有的保留点,构成腐蚀的结果图像。

膨胀算法:

简单说,首先构造一个结构元素B 和B的反射,结构元素的原点定位在背景像素上,当有目标点被覆盖(称之为击中)时,则该点被膨胀为目标点——原点对应的背景像素点被作为新的目标点保留下来,所有的保留点构成膨胀的结果图像。



上面的原理看了挺久的,终于找到一幅好理解的图。。不容易呀。

C++: void erode(InputArray src, OutputArray dst, InputArray kernel, Point anchor=Point(-1,-1),int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& border-
Value=morphologyDefaultBorderValue() )
dilate同上。
结构元素默认3*3矩阵, 原点默认在中心处, 次数默认为1次。
咦,矩阵内容的在哪里,什么十字形什么的。
对前景腐蚀就是对背景膨胀,对前影膨胀就是对背景腐蚀。

开运算和闭运算:
开运算: 先腐蚀,后膨胀。
闭运算: 先膨胀,后腐蚀。
开闭运算的优点: 腐蚀或者膨胀算法非常好,但是改变了原目标物的大小。  开闭运算可以达到腐蚀,膨胀的效果,并且保持目标物大小基本保持不变。
C++: void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor=Point(-1,-1), int iterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )


二  图像边缘探测和角探测。

1边缘探测原理:  

原图作一次膨胀,原图作一次腐蚀,二者相减即是边缘图。    用的这个结构元素,被称为图像梯度。。不同的图像梯度,可以探测不同粗细的边。。(貌似是这样子,详见下一章)

可以用 cv::morphologyEx(image,mat_edge,cv::MORPH_GRADIENT,cv::Mat())  函数直接实现。也可以手动膨胀腐蚀再相减。。

// 边缘检测, 一个膨胀 减去 一个腐蚀 相减就可以。
    //探测边,只要用梯度算法就可以了。你敢信。。
    cv::Mat mat_edge;
    cv::morphologyEx(image,mat_edge,cv::MORPH_GRADIENT,cv::Mat());
    /* 上面梯度算法具体实现,膨胀一次,腐蚀一次,两个相减就得出边。
    cv::Mat tmp;
    cv::dilate(image,mat_edge,cv::Mat());
    cv::erode(image,tmp,cv::Mat());
    cv::absdiff(mat_edge,tmp,mat_edge);
*/
    cv::namedWindow("win_edge");
    cv::imshow("win_edge",mat_edge);

    cout<<"type = "<<mat_edge.type()<<endl;
    cv::Mat mat_thre;
    //二值化一下。变成黑白图,更加清楚
    cv::threshold(mat_edge,mat_thre,40,255,cv::THRESH_BINARY);
    //黑白对调一次 - -
    cv::bitwise_not(mat_thre,mat_thre);
    cv::namedWindow("win_edge_thre");
    cv::imshow("win_edge_thre",mat_thre);


2. 角探测原理:

角探测没有直接调用的函数,检测角的算法是
1>.原图膨胀十字,用结果腐蚀菱形,
2>.原图膨胀x形,用结果腐蚀正方向  1- 2 所得即是。


以下内容转自: http://blog.csdn.net/sunny2038/article/details/9137759#t0
边缘检测:
形态学检测边缘的原理很简单,在膨胀时,图像中的物体会想周围“扩张”;腐蚀时,图像中的物体会“收缩”。比较这两幅图像,由于其变化的区域只发生在边缘。所以这时将两幅图像相减,得到的就是图像中物体的边缘。
角检测:
先用十字形的结构元素膨胀像素,这种情况下只会在边缘处“扩张”,角点不发生变化。接着用菱形的结构元素腐蚀原图像,导致只有在拐角处才会“收缩”,而直线边缘都未发生变化。
第二步是用X形膨胀原图像,角点膨胀的比边要多。这样第二次用方块腐蚀时,角点恢复原状,而边要腐蚀的更多。所以当两幅图像相减时,只保留了拐角处。

//拐角要用到4种图形的结构元素。
    cv::Mat cross(5,5,CV_8U,cv::Scalar(0)),  //十字
            diamond(5,5,CV_8U,cv::Scalar(1)),  //菱形
            square(5,5,CV_8U,cv::Scalar(1)), //正方形
            x(5,5,CV_8U,cv::Scalar(0)); // x 形。
    // 十字初始化。     中间十字为1,其它为0
    for(int i=0;i<5;++i)
    {
        cross.at<uchar>(2,i) = 1;
        cross.at<uchar>(i,2) = 1;
    }
    // 菱形初始化  4个角,均有三个像素置成0,其它全为1
    diamond.at<uchar>(0,0)= 0;
      diamond.at<uchar>(0,1)= 0;
      diamond.at<uchar>(1,0)= 0;
      diamond.at<uchar>(4,4)= 0;
      diamond.at<uchar>(3,4)= 0;
      diamond.at<uchar>(4,3)= 0;
      diamond.at<uchar>(4,0)= 0;
      diamond.at<uchar>(4,1)= 0;
      diamond.at<uchar>(3,0)= 0;
      diamond.at<uchar>(0,4)= 0;
      diamond.at<uchar>(0,3)= 0;
      diamond.at<uchar>(1,4)= 0;

      // 正方形全为1

      // x形。 对角线为1,其它全为0
      for(int i=0;i<5;++i)
      {
          x.at<uchar>(i,i)=1;
          x.at<uchar>(i,4-i)=1;
      }

      //检测角的算法是:
      // 1.原图膨胀十字,用结果腐蚀菱形,
      // 2.原图膨胀x形,用结果腐蚀正方向  1-2所得即是。
      cv::Mat tmp1;
      cv::dilate(image,tmp1,cross);
      cv::erode(tmp1,tmp1,diamond);

      cv::Mat tmp2;
      cv::dilate(image,tmp2,x);
      cv::erode(tmp2,tmp2,square);


      cv::Mat result_corner;
      cv::absdiff(tmp1,tmp2,result_corner);

      //二值化一下。变成黑白图,更加清楚
      cv::threshold(result_corner,result_corner,40,255,cv::THRESH_BINARY);
      cv::namedWindow("win_corner");
      cv::imshow("win_corner",result_corner);


三: 图像分割算法。

1. 分水岭 图像分割算法。 (没有实现,感觉暂时不会用到,先标记在这里)

2. GrabCut 前景后景分割算法。以下两个博客介绍地十分详细。

http://blog.csdn.net/zouxy09/article/details/8534954
http://blog.csdn.net/lcy9819/article/details/6554864

使用 cv::grabCut 大概的步骤:

1> 划定区域。

2> 用掩码图形,确定哪些肯定是前景,哪些肯定是后景。

3> 调用函数。 得到结果(像素值有4种, 分别是 一定前景、一定后景、可能前景、可能后景)

4> 把一定前景和可能前景的位置标出来。

5> 根据4所得的位置,将前景显示出来。

cv::Mat image = cv::imread("/root/Desktop/photos/group.jpg");
    if( !image.data )
    {
        cout<<"failed in read group.jpg..."<<endl;
        return ;
    }
    cv::Rect rect(0,100,400,200);
    cv::Mat result;
    cv::Mat bgdModel,fgdModel;

    //原图复制一份,画上框。
    cv::Mat image_tmp = image.clone();
    cv::rectangle(image_tmp,rect,cv::Scalar(0,0,255));
    cv::namedWindow("win_image_tmp");
    cv::imshow("win_image_tmp",image_tmp);

    // 执行完算法后, result有四种值,0,1,2,3
    cv::grabCut(image,result,rect,bgdModel,fgdModel,5,cv::GC_INIT_WITH_RECT);

    cv::namedWindow("win_grabcut");
    cv::imshow("win_grabcut",result);

    //每个像素,比较,相等的话置为255     把所有可能是前景的像素点标志出来了。
    cv::Mat mat_cmp;
    cv::compare(result,cv::GC_PR_FGD,mat_cmp,cv::CMP_EQ);

    cv::namedWindow("win_cmp");
    cv::imshow("win_cmp",mat_cmp);

    //把原图按照标志位 复制一份。
    cv::Mat fgd;
    image.copyTo(fgd,mat_cmp);
    cv::namedWindow("win_fgd");
    cv::imshow("win_fgd",fgd);


猜你喜欢

转载自blog.csdn.net/a576323437/article/details/9204013