膨胀(dialation)是一种卷积操作,它将目标像素的值替换为卷积核覆盖区域的局部最大值。就像中值滤波一样,这是个非线性核的例子。通常,膨胀采用的核(kernel)是一个四边形或圆形的实心核,其锚点(anchor point)在中心。
腐蚀(erosion)是和膨胀相反的操作,复式操作计算的是核覆盖范围内的局部最小值。
下图分别解释了形态学膨胀(左)和形态学腐蚀(右)的效果:
膨胀和腐蚀是最基础的形态学变换,它们可以用来消除噪声、元素分割和连接等。总的来说:
OpenCV中用cv::dilate()和cv::erode()函数实现膨胀和腐蚀操作:
void cv::dilate(
cv::InputArray src, //输入图像
cv::OutputArray dst, //输出图像
cv::InputArray element, //核
cv::Point anchor=cv::Point(-1,-1), //锚点
int iterations=1, //迭代次数
int borderType=cv::BORDER_CONSTANT, //边界外推类型
const cv::Scalar& borderValue=cv::morphologyDefaultBorderValue()
);
void cv::erode(
cv::InputArray src, //输入图像
cv::OutputArray dst, //输出图像
cv::InputArray element, //核
cv::Point anchor=cv::Point(-1,-1), //锚点
int iterations=1, //迭代次数
int borderType=cv::BORDER_CONSTANT, //边界外推类型
const cv::Scalar& borderValue=cv::morphologyDefaultBorderValue()
);
cv::dilate()和cv::erode()都需要传入源图像和目标图像,且都支持就地调用(源图像和目标图像为同一幅图像)。第三个参数是核,可以传递一个未被初始化的cv::Mat(),不过这会导致函数使用默认的锚点在中心的3×3核。第四个是迭代次数,若不为1,则会自动重复多次调用这个函数。
下面是分别对曾用过的国际象棋图像、校徽以及棋盘进行了膨胀和腐蚀操作,其中棋盘的操作尤为明显:
可以看出,膨胀或是“最大化操作”将明亮的区域扩张并连同;腐蚀或是“最小化操作”将明亮的区域隔离并收缩。
由此可以得出:膨胀操作通常用于发现连通分支(大的颜色或强度相似像素的离散区域);腐蚀操作通常用于消除图中斑点一样的噪声,原理是斑点经过腐蚀后会消失,而大的区域不会受到影响。
开操作:先将图像腐蚀,然后对腐蚀的结果膨胀。开操作常用于对二值图像中的区域进行技术。
闭操作:先将图像膨胀,然后对膨胀的结果腐蚀。闭操作用于复杂连通分支算法中减少无用或噪声驱动的片段。
当对一幅非布尔型图像进行形态学操作时,闭操作最明显的效果是消除小于领域内的点的孤立异常值,开操作消除的是大于领域内点的孤立异常值。
下图分别展示了开操作(左)和闭操作(右)的效果:
下面分别对刚才用过的棋盘进行了开操作和闭操作:
我们可以发现对一幅图像进行形态学开操作:细微的明亮区域被消除,其余明亮区域被孤立,但大小保持不变;对一幅图像进行形态学闭操作:明亮区域被连通同时保持基本大小。
通过比较我们可以发现,膨胀虽然也将明亮区域连通,但已无法保持基本大小。
形态学梯度可以由公式表示:gradient(src)=dilate(src)-erode(src)。即梯度操作的结果等于扩张的结果减腐蚀的结果。
经过梯度操作,图像的明亮边缘被标注。
顶帽和黑帽操作分别用于显示与其邻域相比更亮或更暗的部分,可以用公式表示如下:
TopHat(src)=src-open(src);BlackHat=close(src)-src.
源图像减去对其开操作的图像得到了比源图像更亮的环绕部分及顶帽操作;相反,黑帽操作显示的是比源图像更暗的环绕部分。
int main(int argc, char**argv) {
Mat src = imread("D://somephotos//wq.jpg"),ker, dst1, dst2, dst3, dst4, dst5, dst6, dst7, dst8, dst9, dst0;
Point anchor = Point(-1, -1);
imshow("original", src);
dilate(src, dst1, ker, anchor, 1, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
erode(dst1, dst2, ker, anchor, 1, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
erode(src, dst3, ker, anchor, 1, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
dilate(dst3, dst4, ker, anchor, 1, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
dilate(src, dst5, ker, anchor, 1, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
erode(src, dst6, ker, anchor, 1, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
subtract(dst5, dst6,dst7);
subtract(dst7, dst6, dst8);
subtract(src, dst4, dst9);
subtract(dst2, src, dst0);
imshow("opening", dst4);
imshow("closing", dst2);
imshow("dilate", dst5);
imshow("erode", dst6);
imshow("gradient", dst7);
imshow("margin", dst8);
imshow("tophat", dst9);
imshow("blackhat", dst0);
waitKey(0);
return 0;
}