OpenCV - 图形图像处理 - 形态学处理 1931 1 Opencv图像识别从零到精通(17)----开运算、闭运算、顶帽、黑帽、形态学梯度、形态学角点、细化、填充 作者:qq_208236
2017-10-13 12:15
1191 查看
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) { // 查找所有轮廓 c752 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) figure subplot(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回复
公司简介|招贤纳士|广告服务|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈网站客服杂志客服微博客服webmaster@csdn.net400-660-0108|北京创新乐知信息技术有限公司
版权所有|江苏知之为计算机有限公司|江苏乐知网络技术有限公司京 ICP 证 09002463 号|Copyright © 1999-2016, CSDN.NET, All Rights Reserved
相关文章推荐
- Opencv图像识别从零到精通(17)----开运算、闭运算、顶帽、黑帽、形态学梯度、形态学角点、细化、填充
- OpenCV 形态学图像处理 开运算、闭运算、形态学梯度、顶帽、黑帽合辑(轨迹条控制)综合示例
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- (未实用 需多个函数配套使用)opencv3.0 函数学习 10——morphologyEx 形态学图像处理:开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- OpenCV之十一 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 学习【OpenCV入门教程之十一】 形态学图像处理(二)开运算,闭运算,梯度运算,顶帽,黑帽---思维导图笔记
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- [转]【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV3图像处理】形态学 --- 膨胀、腐蚀、开运算 闭运算、形态学梯度、顶帽运算、黑帽运算
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑
- 【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑