主要形态学操作有 膨胀、腐蚀 、开运算,闭运算,形态学梯度,顶帽,黑帽。对其原理及opencv的实现进行总结。
参考博客及资料如下:图像处理--形态学https://blog.csdn.net/yangleo1987/article/details/53168423 http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/table_of_content_imgproc/table_of_content_imgproc.html
形态学,即数学形态学(mathematical Morphology),是图像处理中应用最为广泛的技术之一,主要用于从图像中提取对表达和描绘区域形状有意义的图像分量,使后续的识别工作能够抓住目标对象最为本质〈最具区分能力-most discriminative)的形状特征,如边界和连通区域等。同时像细化、像素化和修剪毛刺等技术也常应用于图像的预处理和后处理中,成为图像增强技术的有力补充。
膨胀与腐蚀是图像形态学中最基本的操作,也是其他形态学操作的基础。
膨胀与腐蚀能够实现以下作用:
(1)消除噪声
(2)分割出独立的图像元素,在图像中连接相邻的元素
(3)寻找图像中的明显的极大值区域或者极小值区域
(4)求出图像的梯度
腐蚀和膨胀都是对图像的白色部分(高亮部分)而言。膨胀是图像中的高亮部分进行膨胀,类似于领域扩张,效果图拥有比原图更大的高亮区域;腐蚀是原图的高亮部分被腐蚀,类似于领域被蚕食,效果图拥有比原图更小的高亮区域。
从数学的角度来说,膨胀和腐蚀操作就是将图像与核进行卷积,核可以是任意形状和大小的。
1. 腐蚀(erode)
腐蚀就是求局部最小值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最小值,并把这个最小值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐减少。
2.膨胀(dilate)
膨胀就是求局部最大值的操作。核B与图像卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中高亮区域逐渐增长。
膨胀和腐蚀的opencv实现如下:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "highgui.h"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/// 全局变量
Mat src, erosion_dst, dilation_dst;
int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;
/** Function Headers */
void Erosion( int, void* );
void Dilation( int, void* );
/** @function main */
int main( int argc, char** argv )
{
/// Load 图像
src = imread( argv[1] );
if( !src.data )
{ return -1; }
/// 创建显示窗口
namedWindow( "Erosion Demo", CV_WINDOW_AUTOSIZE );
namedWindow( "Dilation Demo", CV_WINDOW_AUTOSIZE );
cvMoveWindow( "Dilation Demo", src.cols, 0 );
/// 创建腐蚀 Trackbar
createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",
&erosion_elem, max_elem,
Erosion );
createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
&erosion_size, max_kernel_size,
Erosion );
/// 创建膨胀 Trackbar
createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",
&dilation_elem, max_elem,
Dilation );
createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
&dilation_size, max_kernel_size,
Dilation );
/// Default start
Erosion( 0, 0 );
Dilation( 0, 0 );
waitKey(0);
return 0;
}
/** @function Erosion */
void Erosion( int, void* )
{
int erosion_type;
if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement( erosion_type,
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size, erosion_size ) );
/// 腐蚀操作
erode( src, erosion_dst, element );
imshow( "Erosion Demo", erosion_dst );
}
/** @function Dilation */
void Dilation( int, void* )
{
int dilation_type;
if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement( dilation_type,
Size( 2*dilation_size + 1, 2*dilation_size+1 ),
Point( dilation_size, dilation_size ) );
///膨胀操作
dilate( src, dilation_dst, element );
imshow( "Dilation Demo", dilation_dst );
}
运用膨胀和腐蚀这两个基本操作,可实现较高级的形态学变换,如 开运算,闭运算,形态学梯度,顶帽,黑帽。
3.开运算
开运算其实就是先腐蚀再膨胀。
开运算可以用来消除小物体,在纤细处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。
4.闭运算
闭运算其实就是先膨胀再腐蚀。闭运算能够排除小型黑洞(黑色区域)。
5形态学梯度
形态学梯度就是膨胀图与腐蚀图之差。
对二值图像进行这一操作,可以将团块的边缘突出出来,我们可以用形态梯度来保留物体的边缘轮廓。
6.顶帽
顶帽就是原图与开运算图之差。
因为开运算带来的结果是放大了裂痕或者局部低亮度的区域。因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小有关。
顶帽运算往往用来分离比邻近点亮一些的斑块,在一幅图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
7 黑帽
黑帽就是原图与闭运算图之差。
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作与核的大小有关。
黑帽运算用来分离比临近点暗一点的斑块,效果图有着非常完美的轮廓。
#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* );
/** @函数 main */
int main( int argc, char** argv )
{
/// 装载图像
src = imread( argv[1] );
if( !src.data )
{ return -1; }
/// 创建显示窗口
namedWindow( window_name, CV_WINDOW_AUTOSIZE );
/// 创建选择具体操作的 trackbar
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 );
/// 创建选择内核形状的 trackbar
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,
&morph_elem, max_elem,
Morphology_Operations );
/// 创建选择内核大小的 trackbar
createTrackbar( "Kernel size:\n 2n +1", window_name,
&morph_size, max_kernel_size,
Morphology_Operations );
/// 启动使用默认值
Morphology_Operations( 0, 0 );
waitKey(0);
return 0;
}
/**
* @函数 Morphology_Operations
*/
void Morphology_Operations( int, void* )
{
// 由于 MORPH_X的取值范围是: 2,3,4,5 和 6
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 );
}