膨胀、腐蚀、开、闭运算是数学形态学最基本的变换。
- 膨胀:把二值图像各1像素连接成分的边界扩大一层(填充边缘或0像素内部的孔);
- 腐蚀:把二值图像各1像素连接成分的边界点去掉从而缩小一层(可提取骨干信息,去掉毛刺,去掉孤立的0像素);
- 开:先腐蚀再膨胀,可以去掉目标外的孤立点
- 闭:先膨胀再腐蚀,可以去掉目标内的孔。
二值形态学概念
数学形态学中二值图像的形态变换是一种针对集合的处理过程。
其形态算子的实质是表达物体或形状的集合与结构元素间的相互作用,结构元素的形状就决定了这种运算所提取的信号的形状信息。形态学图像处理是在图像中移动一个结构元素,然后将结构元素与下面的二值图像进行交、并等集合运算。
基本的形态运算是腐蚀和膨胀。
在形态学中,结构元素是最重要最基本的概念。
结构元素在形态变换中的作用相当于信号处理中的“滤波窗口”。用B(x)——代表结构元素,对工作空间E中的每一点x,腐蚀和膨胀的定义为:
- 用B(x)对E进行腐蚀的结果就是把结构元素B平移后使B包含于E的所有点构成的集合。
- 用B(x)对E进行膨胀的结果就是把结构元素B平移后使B与E的交集非空的点构成的集合。
- 先腐蚀后膨胀的过程称为开运算。它具有消除细小物体,在纤细处分离物体和平滑较大物体边界的作用。
- 先膨胀后腐蚀的过程称为闭运算。它具有填充物体内细小空洞,连接邻近物体和平滑边界的作用。
形态学操作
1、图像腐蚀
卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都为1,那么中心元素就保持原来的像素值,否则为0。
根据卷积核的大小靠近前景的所有像素都会被腐蚀掉(变为0),所以前景物体会变小,整幅图像的白色区域会减少。这对于去除白噪声很有用。
-
说明
通过特定的结构元素腐蚀图像。该函数使用指定的结构元素腐蚀源图像,该结构化元素确定在其上采用最小值的像素邻域的形状:
可以进行多次(迭代)腐蚀。在多通道图像的情况下,每个通道都是独立处理的。 -
声明
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 输入图像,通道数可以是任意的,但深度应该为CV_8U,CV_16U,CV_16S,CV_32F或者CV_64F之一。 dst 输出与src大小和类型相同的图像。 kernel 用于侵蚀的结构元素;如果为element=Mat(),3 x 3则使用矩形结构元素。可以使用getStructuringElement创建内核 anchor 锚在元素内的位置;默认值(-1,-1)表示锚点位于元素中心。 iterations 施加腐蚀的次数。 borderType borderValue 边界不变时候的边界值。 -
应用
void erodeMethod(Mat &src) { imshow("src", src); Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); erode(src, src, kernel, Point(-1, -1)); imshow("erode", src); }
2、图像膨胀
与腐蚀相反,与卷积核对应的原图像的像素值中只要有一个是 1,中心元 素的像素值就是 1。所以这个操作会增加图像中的白色区域(前景)。一般在去噪声时先用腐蚀再用膨胀
-
说明
通过使用特定的结构元素来扩展运行长度编码的二进制图像。该函数使用指定的结构元素对源图像进行扩展,该结构元素确定在其上取最大值的像素邻域的形状:
dst ( x , y ) = max ( x ′ , y ′ ) : element ( x ′ , y ′ ) ≠ 0 src ( x + x ′ , y + y ′ ) \texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y') dst(x,y)=(x′,y′):element(x′,y′)=0maxsrc(x+x′,y+y′)
可以进行几次(迭代)扩张。在多通道图像的情况下,每个通道都是独立处理的。 -
声明
void dilate( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );
-
参数
src 输入图像,通道可以任意,但深度应该为CV_8U,CV_16U,CV_16S,CV_32F或者CV_64F之一。 dst 输出与src大小和类型相同的图像。 kernel 用于扩张的结构元素;如果elemenat = Mat(),则使用3 x 3的矩形结构元素。可以使用getStructuringElement创建内核。 anchor 锚在元素内的位置;默认值(-1,-1)表示锚点位于元素中心。 iteration 扩张的次数。 borderType 参见BorderType。 borderValue 边界不变时的边界值。 -
应用
void dilateMethod(Mat &src) { imshow("src", src); Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); dilate(src, src, kernel, Point(-1, -1)); imshow("dilate", src); }
3、形态学操作
利用morphologyEx这个函数可以方便的对图像进行一系列的膨胀腐蚀组合。
3.1 函数说明
-
说明
执行高级形态转换。函数cv :: morphologyEx可以使用侵蚀和膨胀作为基本操作来执行高级形态转换。
在多通道图像的情况下,每个通道都是独立处理的。
-
声明
void 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() );
-
参数
src 源图像。通道数可以是任意的。深度应为CV_8U,CV_16U,CV_16S,CV_32F或CV_64F之一。 dst 与源图像大小和类型相同的目标图像。 p 形态学操作的类型,请参见MorphTypes kernel 结构元素。可以使用getStructuringElement创建它。 anchor 内核的锚位置。负值表示锚点位于内核中心。 iterations 施加腐蚀和膨胀的次数。 borderType 边缘类型,默认为BORDER_CONSTANT。请参见BorderTypes。 borderValue 边界不变时的边界值。默认值具有特殊含义。 参数op:操作的类型。
enum MorphTypes{ MORPH_ERODE = 0, //腐蚀 MORPH_DILATE = 1, //膨胀 MORPH_OPEN = 2, //开操作 MORPH_CLOSE = 3, //闭操作 MORPH_GRADIENT = 4, //梯度操作 MORPH_TOPHAT = 5, //顶帽操作 MORPH_BLACKHAT = 6, //黑帽操作 MORPH_HITMISS = 7 };
迭代次数是将应用腐蚀或膨胀操作的次数。例如,具有两个迭代的打开操作(MORPH_OPEN)等效于依次应用:腐蚀->腐蚀->膨胀->膨胀(而不是腐蚀->膨胀->腐蚀->膨胀)。
3.2、操作类型
①MORPH_ERODE(腐蚀)
与erode函数的腐蚀效果相同。
②MORPH_DILATE(膨胀)
与dilate函数的膨胀效果相同。
③MORPH_OPEN(开)
- 功能
△消除小物体;
△在纤细处分离物体;
△平滑较大的边界并不明显改变其面积;数学公式:
d s t = o p e n ( s r c , e l e m e m t ) = d i l a t e ( e r o d e ( s r c , e l e m e n t ) ) dst=open(src,elememt)=dilate(erode(src,element)) dst=open(src,elememt)=dilate(erode(src,element))
- 说明
void openMethod(Mat &src) { Mat dst; Mat kernel = getStructuringElement(MORPH_RECT,Size(5,5)); morphologyEx(src,dst,MORPH_OPEN , kernel); imshow("dst", dst); } int main() { Mat src = imread("D:/test/weixin.jpg"); if (src.empty()) { cout << "图片打开失败!" << endl; } imshow("src", src); openMethod(src); waitKey(0); return 0; }
④ MORPH_CLOSE(闭)
- 功能
消除小型黑洞(黑斑)。数学公式
d s t = c l o s e ( s r c , e l e m e n t ) = e r o d e ( d i l a t e ( s r c , e l e m e n t ) ) dst=close(src,element)=erode(dilate(src,element)) dst=close(src,element)=erode(dilate(src,element)) - 应用
void closeMethod(Mat& src) { Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); morphologyEx(src, src, MORPH_CLOSE, kernel); imshow("close", src);
⑤ MORPH_GRADIENT(梯度)
- 功能
对二值图像进行这一操作可以将团块(blob)的边缘突出来。我们可以用形态学梯度来保留物体的边缘轮廓。数学公式
d s t = m o r p h _ g r a d i e n t ( s r c , e l e m e n t ) = d i l a t e ( s r c , e l e m e m t ) − e r o d e ( s r c , e l e m e n t ) dst=morph\_gradient(src,element)=dilate(src,elememt)-erode(src,element) dst=morph_gradient(src,element)=dilate(src,elememt)−erode(src,element)
- 应用
void gradientMethod(Mat& src) { Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); morphologyEx(src, src, MORPH_GRADIENT, kernel); imshow("gradient", src); }
- 应用
⑥MPRPH_TOPHAT(顶帽)
-
功能
因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原型轮廓周围的区域更明亮的区域。且这一操作和选择的核的大小有关。顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的时候,可以用顶帽运算进行背景提取。
数学公式
为原图像与上文刚介绍的“开运算”的结果图之差,表达式如下:
d s t = t o p h a t ( s r c , e l e m e n t ) = s r c − o p e n ( s r c , e l e m e n t ) dst=tophat(src,element)=src-open(src,element) dst=tophat(src,element)=src−open(src,element)
-
应用
void tophatMethod(Mat& src) { Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); morphologyEx(src, src, MORPH_TOPHAT, kernel); imshow("gradient", src); }
⑦MORPH_BLACKHAT(黑帽)
-
功能
黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。
所以,黑帽运算用来分离比邻近点暗一些的斑块。数学表达式:
d s t = b l a c k h a t ( s r c , e l e m e n t ) = c l o s e ( s r c , e l e m e n t ) − s r c dst=blackhat(src,element)=close(src,element)-src dst=blackhat(src,element)=close(src,element)−src
-
应用
void blackhatMethod(Mat& src) { Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1)); morphologyEx(src, src, MORPH_BLACKHAT, kernel); imshow("gradient", src); }
举例应用
Mat src,dst1, dst2,dst3,dst4,dst5,dst6,dst7;
int step_size = 3;
int max_size = 21;
void my_dilate(int ,void*) {
int s = step_size * 2 + 1;
Mat structingElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
dilate(src, dst1, structingElement);
imshow("dilate", dst1);
return;
}
void my_erode(int, void*) {
int s = step_size * 2 + 1;
Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s));
erode(src, dst2, kernel);
imshow("erode", dst2);
return;
}
void my_open(int, void*) {
int s = step_size * 2 + 1;
Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s));
morphologyEx(src, dst3, MORPH_OPEN, kernel);
imshow("open", dst3);
}
void my_close(int, void*) {
int s = step_size * 2 + 1;
Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s));
morphologyEx(src, dst4, MORPH_CLOSE, kernel);
imshow("close", dst4);
}
void my_gradient(int, void*) {
int s = step_size * 2 + 1;
Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s));
morphologyEx(src, dst5, MORPH_GRADIENT, kernel);
imshow("gradient", dst5);
}
void my_tophat(int, void*) {
int s = step_size * 2 + 1;
Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s));
morphologyEx(src, dst6, MORPH_TOPHAT, kernel);
imshow("tophat", dst6);
}
void my_blackhat(int, void*) {
int s = step_size * 2 + 1;
Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s));
morphologyEx(src, dst7, MORPH_BLACKHAT, kernel);
imshow("blackhat", dst7);
}
int main() {
src = imread("D:\\jing.png");
if (src.empty()) {
cerr << "open error" << endl;
return -1;
}
imshow("cat", src);
my_dilate(0, 0);
my_erode(0, 0);
my_open(0, 0);
my_close(0, 0);
my_gradient(0, 0);
my_tophat(0, 0);
my_blackhat(0, 0);
createTrackbar("erode_size", "erode", &step_size, max_size, my_erode);
createTrackbar("erode_size", "dilate", &step_size, max_size,my_dilate);
createTrackbar("erode_size", "open", &step_size, max_size, my_open);
createTrackbar("erode_size", "close", &step_size, max_size, my_close);
createTrackbar("erode_size", "gradient", &step_size, max_size, my_gradient);
createTrackbar("erode_size", "tophat", &step_size, max_size, my_tophat);
createTrackbar("erode_size", "blackhat", &step_size, max_size, my_blackhat);
waitKey(0);
}
原图:
-
dilate
白点不断变大
-
erode
白点逐渐变小
-
open
消除小物体;
在纤细处分离物体;
平滑较大的边界并不明显改变其面积
-
close
祛除小黑点
-
gradient
使轮廓更加清晰
-
顶帽
- 黑帽
好文章分享:
膨胀与腐蚀的彻底击破:https://www.cnblogs.com/daxiongblog/p/6289551.html
学习 opencv—(10)形态学图像处理(2):开运算,闭运算,形态学梯度,顶帽,黒帽合辑 https://www.cnblogs.com/wyuzl/p/6266498.html(NICE)
应用
二值形态膨胀与腐蚀可转化为集合的逻辑运算,算法简单,适于并行处理,且易于硬件实现,适于对二值图像进行图像分割、细化、抽取骨架、边缘提取、形状分析。但是,在不同的应用场合,结构元素的选择及其相应的处理算法是不一样的,对不同的目标图像需设计不同的结构元素和不同的处理算法。结构元素的大小、形状选择合适与否,将直接影响图像的形态运算结果。
- 数学形态学在图像处理中的主要应用包括:边缘检测、图像分割、形态骨架提取、噪声滤除。
- 选取结构元素的方法:多结构元素、遗传算法。