【图像处理笔记】形态学重建
迄今为止讨论的形态学概念只涉及一幅图像和一个或多个结构元。本节讨论一种强大的形态学变换,即形态学重建。形态学重建涉及两幅图像和一个结构元:一幅图像是标记,我们用 F 来表示,它包含重建的起点;另一幅图像是模板,我们用 G来表示,它用来约束重建;结构元用于定义连通性。对于二维应用,连通性通常定义为 8 连通,它由元素都是1的一个3×3结构元暗示。
1 二值图像形态学重建
1.1 测地膨胀和腐蚀
形态学重建的核心是测地膨胀和测地腐蚀的概念。令F表示标记图像,令G表示模板图像。在讨论中,我们假设两幅图像都是二值图像,且F包含于G。标记图像相对于模板大小为1的测地膨胀定义为
F相对于G的大小为n的测地膨胀定义为:
式中,n≥1是整数,D0=F。在这个递归公式中,每一步都是膨胀后取交集。交集运算可以保证模板G限制标记F的生长(膨胀)。下图是大小为1的测地膨胀。
标记F相对于模板G的大小为1的测地腐蚀定义为
标记F相对于模板G的大小为n的测地腐蚀定义为
式中,n≥1是整数,E0=F。在这个递归公式中,每一步都是腐蚀后取并集,以保证图像的测地腐蚀仍然大于模板图像。下图是大小为1的测地腐蚀。
简单地说,测地膨胀和测地腐蚀就是有条件的膨胀和腐蚀。膨胀或腐蚀结果与模板图像进行交集或并集运算,从而对膨胀或腐蚀操作施加了特定的约束。
1.2 腐蚀和膨胀的形态学重建
根据前面的概念,标记图像F相对于模板图像G的膨胀形态学重建,定义为F相对于G的测地膨胀,反复迭代至稳定状态为止。类似地,标记图像F相对于模板图像G的腐蚀形态学重建,定义为F相对于G的测地腐蚀,反复迭代至稳定状态为止。
1.3 应用实例
形态学重建有着很宽的实际应用范围,每种应用都由选择的标记图像和模板图像、所用的结构元及前面定义的形态学运算的组合决定。下面的几个例子说明了这些概念的应用。
1.3.1 重建开运算
在形态学开运算中,腐蚀会删除小目标,而膨胀会试图恢复保留的目标的形状。恢复的精度取决于目标的形状和所用结构元的相似性。重建开运算能够精确地恢复腐蚀后所保留目标的形状。图像F的大小为n的重建开运算定义为,F的大小为n的腐蚀相对于F的膨胀重建,即
重建开运算采用F的腐蚀结果作为膨胀重建的标记。
示例:重建开运算提取竖直长笔画的字符
Mat src = imread("./13.png", 0); Mat erodeImg, dilateImg; // 构造标记图像: 采用图像的腐蚀结果作为膨胀重建的标记 Mat element1 = getStructuringElement(MORPH_RECT, Size(1, 15)); morphologyEx(src, erodeImg, MORPH_ERODE, element1); // 形态学重建 Mat dilateImg_pre = erodeImg.clone(); Mat tmp = Mat::zeros(src.size(), CV_8UC1); Mat cmp = Mat::zeros(src.size(), CV_8UC1); Mat element2 = getStructuringElement(MORPH_RECT, Size(3, 3)); int n = -1; while (n != src.rows*src.cols) { morphologyEx(dilateImg_pre, dilateImg, MORPH_DILATE, element2); bitwise_and(src, dilateImg, dilateImg); compare(dilateImg_pre, dilateImg, cmp, CMP_EQ); n = countNonZero(cmp); dilateImg_pre = dilateImg.clone(); }
1.3.2 填充孔洞的自动算法
令I(x,y)代表一幅二值图像。标记图像F为
于是,
是一幅等于I且所有孔洞被填充的二值图像。
示例:基于重建的孔洞填充
(1)构造标记图像 F 作为膨胀重建的标记,标记图像的边框位置为1-I(x,y),其它位置均为 0;
(2)使用十字形结构元( MORPH_CROSS),对标记图像 F 进行膨胀恢复;
(3)用原图像的补集作为模板来约束重建,与膨胀恢复图像进行逻辑与;
(4)重复图像 F 的重构运算,直到达到稳定收敛状态;
(5)对收敛的标记图像 F 求补,得到孔洞填充的重建结果。
Mat src = imread("./14.png", 0); Mat thrImg, dilateImg, dst; threshold(src, thrImg, 90, 255, THRESH_BINARY_INV); Mat F = thrImg.clone(); F({ 1,1,F.cols - 2,F.rows - 2 }).setTo(0); Mat F_pre = F.clone(); Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3)); Mat cmp = Mat::zeros(src.size(), CV_8UC1); //判断是否达到稳定收敛状态 int n = -1; while (n != src.cols*src.rows) { morphologyEx(F, dilateImg, MORPH_DILATE, element); // 膨胀重建 bitwise_and(thrImg, dilateImg, F); // 约束重建 compare(F, F_pre, cmp, CMP_EQ); // F(n) = F(n - 1) ? n = countNonZero(cmp); F_pre = F.clone(); } bitwise_not(dilateImg, dst); //对收敛的F求补得到孔洞填充的重建结果
1.3.3 边界清除
对于后续形状分析而言,从图像中提取目标是自动图像处理的基本任务。检测接触边界的目标的算法是一个很有用的功能,因为(1)它可以遮蔽图像,以便为进一步处理保留完整的目标,或者(2)它可以作为视野中出现的部分目标的一个信号。接下来,我们将开发一个基于形态学重建的边界清除程序。在这一应用中,我们使用原图像作为模板,并使用下面的标记图像:
边界清除算法首先计算形态学重建(简单的提取接触边界的目标),然后计算下面的差:
得到一幅其中目标不接触边界的图像X。
示例:清除与边界相连的圈
(1)构造标记图像F作为膨胀重建的标记,标记图像的边框位置为I,其它位置均为0;
(2)使用十字形结构元(MORPH_CROSS),对标记图像 F 进行膨胀恢复;
(3)用原图像作为模板来约束重建,与膨胀恢复图像进行逻辑与;
(4)重复图像 F 的重构运算,直到达到稳定收敛状态;
(5)原图减去F,得到边界清除的重建结果。
Mat src = imread("./2.png", 0); Mat thrImg, dilateImg, dst; threshold(src, thrImg, 90, 255, THRESH_BINARY); Mat F = thrImg.clone(); F({ 1,1,F.cols - 2,F.rows - 2 }).setTo(0); Mat F_pre = F.clone(); Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3)); Mat cmp = Mat::zeros(src.size(), CV_8UC1); //判断是否达到稳定收敛状态 int n = -1; while (n != src.cols * src.rows) { morphologyEx(F, dilateImg, MORPH_DILATE, element); // 膨胀重建 bitwise_and(thrImg, dilateImg, F); // 约束重建 compare(F, F_pre, cmp, CMP_EQ); // F(n) = F(n - 1) ? n = countNonZero(cmp); F_pre = F.clone(); } dst = thrImg - dilateImg;
2 灰度级形态学重建
2.1 测地膨胀和腐蚀
假设f和g是相同大小的灰度图像,且f≤g,即f在图像中任一点的灰度比g在这一点的灰度小。f相对于g的大小为1的测地膨胀定义为
其中,^表示逐点最小算子,b是一个合适的结构元。我们看到,大小为1的测地膨胀的获得方式如下:首先计算b对f的膨胀,然后选择(x,y)处膨胀结果和g之间的最小者。f相对于g的大小为n的测地膨胀定义为
类似的,f相当于g的大小为1的测地腐蚀定义为
其中,v表示逐点最大算子。f相对于g的大小为n的测地腐蚀定义为
灰度图像重建开运算:
先腐蚀输入图像,然后将腐蚀后的图像作为标记图像,将图像本身作为模板,进行膨胀,反复迭代至稳定。
重建闭运算:
先膨胀再腐蚀,最后求图像的补集。
2.2 应用实例
示例:基于灰度形态学的复杂背景图像重建
#include <opencv2/opencv.hpp> using namespace cv; //重建开运算 void openRestruct(Mat& src, Size erode_size, Size dilate_size, Mat& dst) { Mat erodeImg; Mat element = getStructuringElement(MORPH_RECT, erode_size); morphologyEx(src, erodeImg, MORPH_ERODE, element); Mat erodeImg_pre = erodeImg.clone(); Mat temp = erodeImg.clone(); Mat cmp = Mat::zeros(src.size(), CV_8UC1); Mat element1 = getStructuringElement(MORPH_RECT, dilate_size); int n = -1; while (n != src.cols * src.rows) { morphologyEx(erodeImg_pre, temp, MORPH_DILATE, element1); bitwise_and(temp, src, temp); compare(temp, erodeImg_pre, cmp, 0); n = countNonZero(cmp); erodeImg_pre = temp.clone(); } dst = temp; } // 重建膨胀 void dilateRestruct(Mat& mark, Mat& mould, Size dilate_size, Mat& dst) { Mat dilateImg_pre = mark.clone(); Mat temp = mark.clone(); Mat cmp = Mat::zeros(mould.size(), CV_8UC1); Mat element = getStructuringElement(MORPH_RECT, dilate_size); int n = -1; while (n != mould.cols * mould.rows) { morphologyEx(dilateImg_pre, temp, MORPH_DILATE, element); bitwise_and(temp, mould, temp); compare(temp, dilateImg_pre, cmp, 0); n = countNonZero(cmp); dilateImg_pre = temp.clone(); } dst = temp; } int main() { Mat src = imread("./1.tif", 0); Mat R1; //(1)重建开运算,抑制按键顶部的水平反射。 openRestruct(src, Size(71, 1), Size(3, 3), R1); // (2) 顶帽变换 Mat tophatImg = src - R1; // (3) 重建开运算,删除按键边缘的垂直反射 openRestruct(tophatImg, Size(11, 1), Size(3, 3), R1); // (4)对重建图像进行水平膨胀,膨胀后的字符与被抑制字符所占的区域重叠。 Mat dilateImg; Mat element = getStructuringElement(MORPH_RECT, Size(11,1)); morphologyEx(R1, dilateImg, MORPH_DILATE, element); // (5) 对水平膨胀图像和顶帽运算图像按位与,即逐点求最小值,复原被抑制的字符 Mat andImg; bitwise_and(dilateImg, tophatImg, andImg); // (6) 以按位与图像作为标记,用重建顶帽图像作模板,进行膨胀重建,得到最终的形态学重建图像。 Mat dst; dilateRestruct(andImg, tophatImg, Size(3, 3), dst); return 0; }
(和参考文献一样,按照书上的步骤效果一般)
参考:
1. 冈萨雷斯《数字图像处理(第四版)》Chapter 9 (所有图片可在链接中下载)
2. 《数字图像处理(第四版)》阅读随笔 ch9(形态学重建、灰度级形态学)