您的位置:首页 > 运维架构

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 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐