OpenCV系列学习之图像形态学

     形态学是什么呢,在生物学说的是研究动植物的形态、结构,延伸到图像莫非就是图像的轮廓、骨架、边界等等。形态学的基本操作是腐蚀与膨胀,简单的讲腐蚀就是消除掉某些小东西,膨胀就是扩大或者‘粗化’一些物体。但是,为了彻底搞清楚这两个东东,我们还是得从原理上搞清楚。

1、腐蚀与膨胀的定义

腐蚀:  


B是结构元且有一个中心点,可以理解为移动的window或者卷积核,A是图像。公式表达的是:在图像A上平移结构元B,如果集合B完全包含于A则加入阵元的中心。

具体过程如下:

膨胀:

B是结构元且有一个中心点,可以理解为移动的window或者卷积核,A是图像。公式表达的是:在图像A上平移结构元B,如果集合B和A至少有一个元素重叠,则加入该阵元的像素。

具体过程如下:


从两个原理图:我们可以知道,腐蚀是删掉图像边界的某些像素,膨胀是给图像的边界添加一圈像素。通过基本的操作我们可以对图像进行消除噪声、边界提取、分割出独立的像素以及图像中响铃的像素

2、测试代码与结果

//图像的腐蚀操作
void imageErode(){
	const char* name = "erode.tif";
	IplImage* img = cvLoadImage(name, CV_LOAD_IMAGE_GRAYSCALE);
	if (img == NULL){
		printf("image load failed.\n");
		return;
	}
	int iterationTimes = 1;//迭代 次数

	IplImage* imgDst1 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	//第三个参数是卷积核默认是NULL,即3*3的模板,第四个参数是迭代次数
	cvErode(img, imgDst1, NULL, iterationTimes);

	//定义14*14的卷积核(结构元),中心位置在(6,6)核形状是矩形.由于预处理的部件垂直线条的宽度为11个像素,故卷积核的大小应该大于11
	IplConvKernel* kernel = cvCreateStructuringElementEx(14, 14, 6, 6, CV_SHAPE_RECT);
	IplImage* imgDst2 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	cvErode(img, imgDst2, kernel, iterationTimes);

	
	cvNamedWindow("Source", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("Erode", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("mykernel", CV_WINDOW_AUTOSIZE);
	cvShowImage("Source", img);
	cvShowImage("Erode", imgDst1);
	cvShowImage("mykernel", imgDst2);

	cvWaitKey();
	cvReleaseStructuringElement(&kernel);
	cvReleaseImage(&img);
	cvReleaseImage(&imgDst1);
	cvReleaseImage(&imgDst2);
	cvDestroyAllWindows();
}
结果:  第二张图是使用3*3的模板腐蚀,可以看到除了四条较宽的线没有被腐蚀掉,其它线均被腐蚀掉了。第三张图是使用的14*14的模板,由于加粗 线条大于11个像素,所有需要较大的模板,但是弊端是边上的线条都变细了很多。,,,,所以 ,腐蚀缩小或者细化了图像中的物体, 并且 腐蚀通常用来消除图像中的斑点噪声。


//图像的膨胀操作
void imageDilate(){
	const char* name = "dilate.tif";
	IplImage* img = cvLoadImage(name, CV_LOAD_IMAGE_GRAYSCALE);
	if (img == NULL){
		printf("image load failed.\n");
		return;
	}
	int iterationTimes = 1;//迭代 次数

	IplImage* imgDst1 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	cvDilate(img, imgDst1, NULL, iterationTimes);

	cvNamedWindow("Source", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("Erode", CV_WINDOW_AUTOSIZE);
	
	cvShowImage("Source", img);
	cvShowImage("Erode", imgDst1);
	

	cvWaitKey();
	cvReleaseImage(&img);
	cvReleaseImage(&imgDst1);
	cvDestroyAllWindows();
}
结果:如图所示, 膨胀‘粗化’了图像中的字符,使得断裂的某些地方连接了起来,膨胀最简单的应用就是桥接裂缝

3、开操作与闭操作

开操作是先腐蚀在膨胀,能够平滑轮廓,断开较小的狭隘劲并且消除细小的物体。
闭操作是先膨胀再腐蚀,也能够起到平滑轮廓的作用,与开操作相反,它能够弥合较窄的间断,填补一些空隙。
 OpenCV提供了cvMorphologyEx函数,可以进行多种图像操作,包括开、闭、形态梯度、礼貌、黑帽。
void cvMorphologyEx(const CvArr* src, CvArr* dst, CvArr* tmp, IplConvKernel* element, int operation, int iterations = 1);
src:输入图像      dst:输出图像     tmp:临时图像,某些操作会用到。需要使用tmp时,它应与原图像有同样的大小         element:结构元素      
operation:形态操作的类型,有以下几种可用的类型:
-CV_MOP_OPEN      开运算不需要临时图像
-CV_MOP_CLOSE      闭运算不需要临时图像
-CV_MOP_GRADIENT     形态梯度需要临时图像
-CV_MOP_TOPHAT      “礼帽”src = dst情况下需要
-CV_MOP_BLACKHAT     ”黑帽“src = dst情况下需要

测试代码:
//图像的开闭操作
void imageOpenClose(){
	const char* name = "open.tif";
	IplImage* img = cvLoadImage(name, CV_LOAD_IMAGE_GRAYSCALE);
	if (img == NULL){
		printf("image load failed.\n");
		return;
	}
	int iterationTimes = 1;//迭代 次数

	IplImage* temp = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);

	IplImage* imgDst1 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	//第三个参数是和原图像同样大小的,用于某些操作的中间转换 开闭操作可为空
	//第四个参数是卷积核,默认是3*3
	cvMorphologyEx(img, imgDst1, temp, NULL, CV_MOP_OPEN,iterationTimes);//
	//对于开操作再进行膨胀,可以填补断裂、空隙
	IplImage* imgDst1Better = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	cvDilate(imgDst1, imgDst1Better, NULL, iterationTimes);
	
	IplImage* imgDst2 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	cvMorphologyEx(img, imgDst2, NULL, NULL, CV_MOP_CLOSE, iterationTimes);

	cvNamedWindow("Source", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("open", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("openAndDilate", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("close", CV_WINDOW_AUTOSIZE);

	cvShowImage("Source", img);
	cvShowImage("open", imgDst1);
	cvShowImage("openAndDilate", imgDst1Better);
	cvShowImage("close", imgDst2);


	cvWaitKey();
	cvReleaseImage(&img);
	cvReleaseImage(&imgDst1);
	cvReleaseImage(&imgDst2);
	cvReleaseImage(&imgDst1Better);
	cvDestroyAllWindows();
}

结果分析:对于开操作,可以看到很好的消除了小的噪声(包括背景和指纹本身带的噪声),但是指纹间产生了一些新的断裂。   
对于闭操作,背景噪声完全没有被消除,即使指纹间的噪声被消除了,效果也很差。但是连接性比原图要好。最后一张图是先对图像开操作然后再膨胀,这样弥补了图二产生新的断裂的错误,并且纹路变粗了,虽然不知道这还少利还是弊,哈哈。

4、形态学梯度-边界提取
数字图像处理书中对于边界提取是通过原图像减去腐蚀的图像来提取边界。过程如下:


OpenCV是将膨胀后的图像和腐蚀后的图像做差,即结果= 膨胀-腐蚀 ,得到形态学梯度。个人觉得差别不是很大,先膨胀可能会增加一些边界线条的粗度,可以从结果图中进行观察分析。
测试代码:

void imageGradient(){
	const char* name = "huang.jpg";
	IplImage* img = cvLoadImage(name, CV_LOAD_IMAGE_GRAYSCALE);
	if (img == NULL){
		printf("image load failed.\n");
		return;
	}
	int iterationTimes = 1;//迭代 次数
	//形态学梯度:膨胀-腐蚀
	IplImage* temp = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	IplImage* imgDst1 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	cvMorphologyEx(img, imgDst1, temp, NULL, CV_MOP_GRADIENT, iterationTimes);

	//数字图像处理:边界提取 = 原图像-腐蚀
	IplImage* imgDst2 = cvCreateImage(cvGetSize(img), img->depth, img->nChannels);
	cvErode(img, imgDst2, NULL, iterationTimes);
	cvAbsDiff(img, imgDst2, imgDst2);

	cvNamedWindow("Source", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("Gradient", CV_WINDOW_AUTOSIZE);
	cvNamedWindow("aa", CV_WINDOW_AUTOSIZE);
	cvShowImage("Source", img);
	cvShowImage("Gradient", imgDst1);
	cvShowImage("aa", imgDst2);

	cvWaitKey();
	cvReleaseImage(&img);
	cvReleaseImage(&imgDst1);
	cvDestroyAllWindows();
}
结果图:图二是openCV的自带的函数处理,图三是利用原图像减去腐蚀的图像,可以看到图二的边界乣粗些,这是它先膨胀的原因。



终于写完了,虽然还不是很全,但是对于形态学的处理基本知道了,最近任务重,要学的东西太多,继续更新ing!!!





  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容可以得知OpenCV中的dilate函数是用于图像膨胀操作的函数。下面是该函数的使用方法和参数说明: ```cpp void dilate(InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue()); ``` - InputArray类型的src,输入图像,如Mat类型。 - OutputArray类型的dst,输出图像。 - InputArray类型的kernel,膨胀操作的内核也就是上面所说的蒙版。为NULL时,默认表示以参考点为中心3*3的核。一般配合函数getStructuringElement使用,该函数可以构造一个指定形状和尺寸的蒙版。 - Point类型的anchor,锚点。默认值(-1,-1),表示位于单位中心,一般不用。 - int类型的iterations,迭代使用的次数,默认值为1。 - int类型的borderType,推断图像外部像素的边界模式,我OpenCV版本的默认值为BORDER_CONSTANT。如果图像边界需要扩展,则不同的模式下所扩展的像素,其生成原则不同。 - const Scalar&类型的borderValue,当边界为常数时的边界值,默认值为morphologyDefaultBorderValue()。 下面是一个使用dilate函数进行图像膨胀操作的示例代码: ```cpp cv::Mat test = cv::Mat::zeros(64, 64, CV_8UC1); cv::rectangle(test, cv::Rect(30, 30, 5, 5), 255, -1); cv::Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); cv::Mat result; cv::dilate(test, result, element); imshow("original", test); imshow("result", result); waitKey(0); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值