OpenCV

来源:互联网 发布:nginx php 编辑:程序博客网 时间:2024/05/21 19:21
OpenCV-图形图像处理-形态学处理19311
原文地址:http://lib.csdn.net/article/opencv/38992

Opencv图像识别从零到精通(17)----开运算、闭运算、顶帽、黑帽、形态学梯度、形态学角点、细化、填充

作者:qq_20823641

一、全体成员   

   经过了上一篇的膨胀、腐蚀以后,我们就可以用他们组合起来,形成了更多的形态效果,这样就不会太多的改变原来图像的大小,总结了一下,主要包含开运算、闭运算、顶帽、黑帽、形态学梯度、形态学角点、细化、填充这些方面。

1.开运算

对图像进行先腐蚀后膨胀的操作就是图像的开运算。

它的功能是有利于移走黑色前景下的白色小物体。

2.闭运算

对图像进行先膨胀后腐蚀的操作就是图像的闭运算。

它的功能是有利于移走黑色区域小洞。

3.形态学梯度

形态学梯度是一幅图像腐蚀和膨胀的差值。

它有利于查找图像的轮廓。

4.Top Hat

Top Hat是输入图像和它开运算的差值。

5.Black Hat

Black Hat是输入图像和它闭运算的差值。

6.形态学角点

dst=open-open

7细化

这个比较的复杂,讲原理的话估计要还几页http://www.cnblogs.com/mikewolf2002/p/3321732.html,可以看看这篇文章说的很多,也很好

二、morphologyEx函数

其实,说了那么多大多数都是和一个函数有关系那就是morphologyEx函数,这里面给出了多种形态学类型的定义

<span style="font-size:18px;"><span style="font-size:18px;">void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,                         InputArray kernel, Pointanchor, int iterations,                         int borderType, constScalar& borderValue )  {  //拷贝Mat数据到临时变量     Mat src = _src.getMat(), temp;     _dst.create(src.size(), src.type());     Mat dst = _dst.getMat();     //一个大switch,根据不同的标识符取不同的操作     switch( op )      {     case MORPH_ERODE:  //腐蚀       erode( src, dst, kernel, anchor, iterations, borderType, borderValue );         break;     case MORPH_DILATE:  //膨胀       dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );         break;     case MORPH_OPEN:  //开运算       erode( src, dst, kernel, anchor, iterations, borderType, borderValue );         dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );         break;     case CV_MOP_CLOSE:  //闭运算       dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );         erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );         break;     case CV_MOP_GRADIENT:  //形态学梯度       erode( src, temp, kernel, anchor, iterations, borderType, borderValue );         dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );         dst -= temp;         break;     case CV_MOP_TOPHAT:  //顶帽       if( src.data != dst.data )             temp = dst;         erode( src, temp, kernel, anchor, iterations, borderType, borderValue );          dilate( temp, temp, kernel, anchor,iterations, borderType, borderValue );         dst = src - temp;         break;     case CV_MOP_BLACKHAT:  //黑帽       if( src.data != dst.data )             temp = dst;         dilate( src, temp, kernel, anchor, iterations, borderType, borderValue);         erode( temp, temp, kernel, anchor, iterations, borderType, borderValue);         dst = temp - src;         break;     default:         CV_Error( CV_StsBadArg, "unknown morphological operation" );      }  }  </span></span>

三、开运算小例子

为了好理解,简单说明一个例子,其他的参考,改变参数就可以,下面是使用开运算作为例子

<span style="font-size:18px;"><span style="font-size:18px;">//-----------------------------------------------------------------------------------------------  int main( )  {         Mat image = imread("1.jpg");         namedWindow("【原始图】开运算");          namedWindow("【效果图】开运算");           imshow("【原始图】开运算", image);          Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));            morphologyEx(image,image, MORPH_OPEN, element);           imshow("【效果图】开运算", image);          waitKey(0);          return 0;   }  </span></span>


四、综合成员

相信学习那么长时间,上面的一看就明白了,那么就可以进去综合代码

<span style="font-size:18px;"><span style="font-size:18px;">#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/highgui/highgui.hpp"#include <stdlib.h>#include <stdio.h>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;char* window_name = "Morphology Transformations Demo";void Morphology_Operations( int, void* );int main( int argc, char** argv ){  src = imread( "lena.jpg" );  if( !src.data )  { return -1; } namedWindow( window_name, CV_WINDOW_AUTOSIZE ); 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* ){  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 );  }</span></span>

再看来看看角点检测,其实也是膨胀腐蚀的变形,只是没有一个好名字

<span style="font-size:18px;">Mat result2;dilate(image,result2,x);erode(result2,result2,square);imshow("result2",result2);absdiff(result2,result,result);imshow("result3",result);threshold(result,result,40,255,THRESH_BINARY);</span>


细化,这个比较的复杂,需要好好的理解一下

 图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization) 的一种操作运算。细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。

<span style="font-size:18px;">#include <opencv2/opencv.hpp>#include <opencv2/core/core.hpp>#include <iostream>#include <vector>cv::Mat thinImage(const cv::Mat & src, const int maxIterations = -1){    assert(src.type() == CV_8UC1);    cv::Mat dst;    int width  = src.cols;    int height = src.rows;    src.copyTo(dst);    int count = 0;      while (true)    {        count++;        if (maxIterations != -1 && count > maxIterations)             break;        std::vector<uchar *> mFlag; //用于标记需要删除的点        //对点标记        for (int i = 0; i < height ;++i)        {            uchar * p = dst.ptr<uchar>(i);            for (int j = 0; j < width; ++j)            {                //如果满足四个条件,进行标记                //  p9 p2 p3                //  p8 p1 p4                //  p7 p6 p5                uchar p1 = p[j];                if (p1 != 1) continue;                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);                uchar p8 = (j == 0) ? 0 : *(p + j - 1);                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);                if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)                {                    int ap = 0;                    if (p2 == 0 && p3 == 1) ++ap;                    if (p3 == 0 && p4 == 1) ++ap;                    if (p4 == 0 && p5 == 1) ++ap;                    if (p5 == 0 && p6 == 1) ++ap;                    if (p6 == 0 && p7 == 1) ++ap;                    if (p7 == 0 && p8 == 1) ++ap;                    if (p8 == 0 && p9 == 1) ++ap;                    if (p9 == 0 && p2 == 1) ++ap;                    if (ap == 1 && p2 * p4 * p6 == 0 && p4 * p6 * p8 == 0)                    {                        mFlag.push_back(p+j);                    }                }            }        }        //将标记的点删除        for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)        {            **i = 0;        }        if (mFlag.empty())        {            break;        }        else        {            mFlag.clear();//将mFlag清空        }        for (int i = 0; i < height; ++i)        {            uchar * p = dst.ptr<uchar>(i);            for (int j = 0; j < width; ++j)            {                uchar p1 = p[j];                if (p1 != 1) continue;                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);                uchar p8 = (j == 0) ? 0 : *(p + j - 1);                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);                if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)                {                    int ap = 0;                    if (p2 == 0 && p3 == 1) ++ap;                    if (p3 == 0 && p4 == 1) ++ap;                    if (p4 == 0 && p5 == 1) ++ap;                    if (p5 == 0 && p6 == 1) ++ap;                    if (p6 == 0 && p7 == 1) ++ap;                    if (p7 == 0 && p8 == 1) ++ap;                    if (p8 == 0 && p9 == 1) ++ap;                    if (p9 == 0 && p2 == 1) ++ap;                    if (ap == 1 && p2 * p4 * p8 == 0 && p2 * p6 * p8 == 0)                    {                        //标记                        mFlag.push_back(p+j);                    }                }            }        }        for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)        {            **i = 0;        }        if (mFlag.empty())        {            break;        }        else        {            mFlag.clear();//将mFlag清空        }    }    return dst;}int main(int argc, char*argv[]){    if (argc != 2)    {        std::cout << "参数个数错误!" << std::endl;        return -1;    }    cv::Mat src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);    if (src.empty())    {        std::cout << "读取文件失败!" << std::endl;        return -1;    }    cv::threshold(src, src, 128, 1, cv::THRESH_BINARY);    //图像细化    cv::Mat dst = thinImage(src);    //显示图像    dst = dst * 255;    cv::namedWindow("src1", CV_WINDOW_AUTOSIZE);    cv::namedWindow("dst1", CV_WINDOW_AUTOSIZE);    cv::imshow("src1", src);    cv::imshow("dst1", dst);    cv::waitKey(0);}</span>



填充可以参考这篇文章http://lib.csdn.net/article/opencv/28355

<span style="font-size:18px;">#include "cxcore.h"   #include "cv.h"   #include "highgui.h"     void FillInternalContours(IplImage *pBinary, double dAreaThre)   {       double dConArea;       CvSeq *pContour = NULL;       CvSeq *pConInner = NULL;       CvMemStorage *pStorage = NULL;       // 执行条件       if (pBinary)       {           // 查找所有轮廓           pStorage = cvCreateMemStorage(0);           cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);           // 填充所有轮廓           cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));          // 外轮廓循环           int wai = 0;          int nei = 0;          for (; pContour != NULL; pContour = pContour->h_next)           {               wai++;              // 内轮廓循环               for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)               {                   nei++;                  // 内轮廓面积                   dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));                  printf("%f\n", dConArea);                  if (dConArea <= dAreaThre)                   {                       cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));                  }               }           }           printf("wai = %d, nei = %d", wai, nei);          cvReleaseMemStorage(&pStorage);           pStorage = NULL;       }   }   int Otsu(IplImage* src)      {          int height=src->height;          int width=src->width;                //histogram          float histogram[256] = {0};          for(int i=0; i < height; i++)        {              unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;              for(int j = 0; j < width; j++)             {                  histogram[*p++]++;              }          }          //normalize histogram          int size = height * width;          for(int i = 0; i < 256; i++)        {              histogram[i] = histogram[i] / size;          }            //average pixel value          float avgValue=0;          for(int i=0; i < 256; i++)        {              avgValue += i * histogram[i];  //整幅图像的平均灰度        }             int threshold;            float maxVariance=0;          float w = 0, u = 0;          for(int i = 0; i < 256; i++)         {              w += histogram[i];  //假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例            u += i * histogram[i];  // 灰度i 之前的像素(0~i)的平均灰度值: 前景像素的平均灰度值              float t = avgValue * w - u;              float variance = t * t / (w * (1 - w) );              if(variance > maxVariance)             {                  maxVariance = variance;                  threshold = i;              }          }            return threshold;      }       int main()  {      IplImage *img = cvLoadImage("lena.jpg", 0);      IplImage *bin = cvCreateImage(cvGetSize(img), 8, 1);        int thresh = Otsu(img);      cvThreshold(img, bin, thresh, 255, CV_THRESH_BINARY);        FillInternalContours(bin, 200);        cvNamedWindow("img");      cvShowImage("img", img);        cvNamedWindow("result");      cvShowImage("result", bin);        cvWaitKey(-1);        cvReleaseImage(&img);      cvReleaseImage(&bin);        return 0;  }  </span>


五、matlab辅助

<span style="font-size:18px;">I=imread('d:\22.png');SE=strel('rectangle',[3 3]);I2=imopen(I,SE);I3=imclose(I,SE);I4=im2bw(I);I5=bwmorph(I4,'thin',inf)figuresubplot(131),imshow(I2),title('开运算')subplot(132),imshow(I3),title('闭运算')subplot(133),imshow(I5),title('细化')</span>


图像识别算法交流 QQ群:145076161,欢迎图像识别与图像算法,共同学习与交流

查看原文>>3
看过本文的人也看了:
  • OpenCV知识结构图
  • OpenCV膨胀和腐蚀示例代码
  • Opencv图像的腐蚀与膨胀总结
  • 【OpenCV入门教程之十】 形态学图像处...
  • Kinect2入门+opencv画骨架+骨架数据
  • OpenCV图像处理篇之腐蚀与膨胀
发表评论

1个评论

  • f82978

    讲这么仔细,赞赞赞

    2017-03-21 15:30:42回复