目录
开运算(Opening Operation) = 先腐蚀,后膨胀
闭运算(Closing Operation) = 先膨胀,后腐蚀
概述
形态学变化本质是数学上一个分支,是基于形状的一系列图像处理操作。
基础理论
形态学的主要用途是获取物体拓扑和结果信息,通过物体结构一系列运算,得到物体更本质的形态。在图像处理的主要应用有:
1.利用形态学处理图像,用于改善图像质量目的,比如通过原图像减去顶冒图像用于提高对比度
2.描述和定义图像的各种几何参数和特征,如面积、连通、颗粒度、骨架和方向性。
腐蚀和膨胀
腐蚀和膨胀是对二维图片的进行操作的形态学运算,简单来讲形态学操作就是基于形状的一系列图像处理操作,通过将结构元素作用于输入图像来产生输出图像。腐蚀(Erosion)和膨胀(Dilation)是最基本的形态学操作,他们运用广泛主要有:
1.消除噪声
2.分割(ioslate)独立的图像元素以及连接(join)相邻的元素
3.寻找图像中的明显的极大值区域或极小值区域
腐蚀的原理
二值图像前景物体为1,背景为0.假设原图像中有一个前景物体,那么我们用一个结构元素去腐蚀原图的过程是这样的:遍历原图像的每一个像素,然后用结构元素的中心点对准当前正在遍历的这个像素,然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最小值,用这个最小值替换当前像素值。由于二值图像最小值就是0,所以就是用0替换,即变成了黑色背景。从而也可以看出,如果当前结构元素覆盖下,全部都是背景,那么就不会对原图做出改动,因为都是0.如果全部都是前景像素,也不会对原图做出改动,因为都是1.只有结构元素位于前景物体边缘的时候,它覆盖的区域内才会出现0和1两种不同的像素值,这个时候把当前像素替换成0就有变化了。因此腐蚀看起来的效果就是让前景物体缩小了一圈一样。对于前景物体中一些细小的连接处,如果结构元素大小相等,这些连接处就会被断开。
膨胀的原理
二值图像前景物体为1,背景为0.假设原图像中有一个前景物体,那么我们用一个结构元素去膨胀原图的过程是这样的:遍历原图像的每一个像素,然后用结构元素的中心点对准当前正在遍历的这个像素,然后取当前结构元素所覆盖下的原图对应区域内的所有像素的最大值,用这个最大值替换当前像素值。由于二值图像最大值就是1,所以就是用1替换,即变成了白色前景物体。从而也可以看出,如果当前结构元素覆盖下,全部都是背景,那么就不会对原图做出改动,因为都是0.如果全部都是前景像素,也不会对原图做出改动,因为都是1.只有结构元素位于前景物体边缘的时候,它覆盖的区域内才会出现0和1两种不同的像素值,这个时候把当前像素替换成1就有变化了。因此膨胀看起来的效果就是让前景物体胀大了一圈一样。对于前景物体中一些细小的断裂处,如果结构元素大小相等,这些断裂的地方就会被连接起来。
源码
void dilate( InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() )
void erode ( InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & borderValue = morphologyDefaultBorderValue() )
测试代码
src = cv2.imread('C:/Users/37593/Desktop/timg.jpg', 0)
cv2.imshow("src", src)
# 定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
erode = cv2.erode(src, kernel)
dilate = cv2.dilate(src, kernel)
cv2.imshow("erode", erode)
cv2.imshow("dilate", dilate)
开运算和闭运算
开运算(Opening Operation) = 先腐蚀,后膨胀
腐蚀和膨胀的原理前期讲解过,假设背景是白色区域(较明亮),开运算则是先腐蚀白色,意味前景区域连通了些,后又膨胀,意味白色膨胀,可能将一些噪点去除。结果是删除了不属于结构元素的对象区域,平滑了轮廓,断开了狭窄的连接,去掉细小的突出部分。
闭运算(Closing Operation) = 先膨胀,后腐蚀
dst=close(src,element)=erode(dilate(src,element))
闭运算当然是开运算相反操作,即先膨胀,后腐蚀。能够排除小型黑洞(黑色区域),能够平滑对象的轮廓,但是与开运算不同的是闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。
综上所述,对于开运算和闭运算,比较侧重第一步,开和闭相对于白色来讲,开就是是白色区域断开点(侧重腐蚀,即开运算第一步),闭则相反。
在应用中首先确定目标为白色还是黑色,然后根据是连通还是去燥,最后确定开运算还是闭运算。
测试代码
src = cv2.imread('C:/Users/37593/Desktop/timg.jpg', 0)
cv2.imshow("src", src)
# 定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 开运算
open = cv2.dilate(cv2.erode(src, kernel), kernel)
# 闭运算
close = cv2.erode(cv2.dilate(src, kernel), kernel)
cv2.imshow("open", open)
cv2.imshow("close", close)
# 闭运算
closed = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)
# 显示腐蚀后的图像
cv2.imshow("Close", closed)
# 开运算
opened = cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)
# 显示腐蚀后的图像
cv2.imshow("Open", opened)
形态学梯度
原理
形态梯度是膨胀图与腐蚀图之差,其操作原理表达式如下:
形态学梯度(Morphological Gradient) = 膨胀图 - 腐蚀图
dst=morph_grad(src,element)=dilate(src,element)−erode(src,element)
形态学梯度很容易理解,公式表述为膨胀图-腐蚀图,就是利用白色膨胀,另一幅图像腐蚀,本质就是目标边缘一个左移,一个右移,相减生成里差值图像,即为边缘图像。
测试代码
src = cv2.imread('C:/Users/37593/Desktop/timg.jpg', 0)
cv2.imshow("src", src)
# 定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 腐蚀
erode = cv2.erode(src, kernel)
# 膨胀
dilate = cv2.dilate(src, kernel)
# 形态学梯度
gradient = dilate - erode
cv2.imshow("erode", erode)
cv2.imshow("dilate", dilate)
cv2.imshow("gradient", gradient)
顶帽和黑帽
原理
顶帽操作是原图像与开运算结果图之差。
顶帽(Top Hat) = 原图 - 开运算图
dst=tophat(src,element)=src−open(src,element)
开运算的结果是白色区域断开,因此从原图中减去开运算后的图得到的效果图能够突出比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小有关。
顶帽操作往往用来分离比邻近点亮一些的板块,在一幅图像具有大幅背景而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
黑帽(Black Hat) = 闭运算图 - 原图
dst=blackhat(src,element)=close(src,element)−src
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,这一操作也与选择的核尺寸有关。所以黑帽运算用来分离比邻近点暗一些的斑块,效果图有着非常完美的轮廓。
测试代码
src = cv2.imread('C:/Users/37593/Desktop/timg.jpg', 0)
cv2.imshow("src", src)
# 定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 闭运算
closed = cv2.morphologyEx(src, cv2.MORPH_CLOSE, kernel)
# 开运算
opened = cv2.morphologyEx(src, cv2.MORPH_OPEN, kernel)
# 顶帽
top_hat = src - opened
# 黑帽
black_hat = closed - src
# cv2.imshow("Open", opened)
# cv2.imshow("Close", closed)
cv2.imshow("top_hat", top_hat)
cv2.imshow("black_hat", black_hat)
OpenCV-API
opencv中提供了形态学操作函数morphologyEx()来实现开运算、闭运算、形态学梯度、顶帽、黑帽等五种相对高级的操作,也可以实现膨胀核腐蚀两种基本的形态学操作。
void cv::morphologyEx (InputArray src,
OutputArray dst,
int op,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & borderValue = morphologyDefaultBorderValue()
)
InputArray src: 输入图像,可以是Mat类型,对于图像通道数无要求,但图像深度必须是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F
OutPutArray dst: 目标图像,与原图像尺寸核类型相同
int op: 形态学运算的类型,可以通过MorphTypes查看,如下所示:
标识符 | 运算类型
MORPH_OPEN: 开运算
MORPH_CLOSE :闭运算
MORPH_GRADIENT: 形态学梯度
MORPH_TOPHAT:顶帽运算
MORPH_BLACKHAT: 黑帽运算
MORPH_ERODE :腐蚀运算
MORPH_DILATE :膨胀运算
MORPH_HITMISS: 击中击不中运算(只支持CV_8UC1类型的二值图像)
InputArray kernel: 形态学运算的内核,如果是Mat()则表示的是参考点位于内核中心3x3的核
源码
//--------------------------------------------------------------------------------------------------------
void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,
InputArray kernel, Pointanchor, int iterations,
int borderType, constScalar& borderValue )
{
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" );
}
}
学习时感谢这些资料的帮助:http://www.cnblogs.com/polly333/p/7307602.html#4