迄今为止讨论的形态学概念只涉及一幅图像和一个或多个结构元。本节讨论一种强大的形态学变换,即形态学重建。形态学重建涉及两幅图像和一个结构元:一幅图像是标记,我们用 F 来表示,它包含重建的起点;另一幅图像是模板,我们用 G来表示,它用来约束重建;结构元用于定义连通性。对于二维应用,连通性通常定义为 8 连通,它由元素都是1的一个3×3结构元暗示。

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代

1 二值图像形态学重建

1.1 测地膨胀和腐蚀

形态学重建的核心是测地膨胀和测地腐蚀的概念。令F表示标记图像,令G表示模板图像。在讨论中,我们假设两幅图像都是二值图像,且F包含于G。标记图像相对于模板大小为1的测地膨胀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_02

F相对于G的大小为n的测地膨胀定义为:

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_二值图像_03

式中,n≥1是整数,D0=F。在这个递归公式中,每一步都是膨胀后取交集。交集运算可以保证模板G限制标记F的生长(膨胀)。下图是大小为1的测地膨胀。

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_04

标记F相对于模板G的大小为1的测地腐蚀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_05

标记F相对于模板G的大小为n的测地腐蚀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_06

式中,n≥1是整数,E0=F。在这个递归公式中,每一步都是腐蚀后取并集,以保证图像的测地腐蚀仍然大于模板图像。下图是大小为1的测地腐蚀。

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_二值图像_07

简单地说,测地膨胀和测地腐蚀就是有条件的膨胀和腐蚀。膨胀或腐蚀结果与模板图像进行交集或并集运算,从而对膨胀或腐蚀操作施加了特定的约束。

1.2 腐蚀和膨胀的形态学重建

根据前面的概念,标记图像F相对于模板图像G的膨胀形态学重建,定义为F相对于G的测地膨胀,反复迭代至稳定状态为止。类似地,标记图像F相对于模板图像G的腐蚀形态学重建,定义为F相对于G的测地腐蚀,反复迭代至稳定状态为止。

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_08

1.3 应用实例 

形态学重建有着很宽的实际应用范围,每种应用都由选择的标记图像和模板图像、所用的结构元及前面定义的形态学运算的组合决定。下面的几个例子说明了这些概念的应用。

1.3.1 重建开运算

在形态学开运算中,腐蚀会删除小目标,而膨胀会试图恢复保留的目标的形状。恢复的精度取决于目标的形状和所用结构元的相似性。重建开运算能够精确地恢复腐蚀后所保留目标的形状。图像F的大小为n的重建开运算定义为,F的大小为n的腐蚀相对于F的膨胀重建,即

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_09

重建开运算采用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();
}

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_二值图像_10

1.3.2 填充孔洞的自动算法

令I(x,y)代表一幅二值图像。标记图像F为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_11

于是,

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_12

是一幅等于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求补得到孔洞填充的重建结果

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_13

1.3.3 边界清除

检测接触边界的目标的算法是一个很有用的功能,因为(1)它可以遮蔽图像,以便为进一步处理保留完整的目标,或者(2)它可以作为视野中出现的部分目标的一个信号。接下来,我们将开发一个基于形态学重建的边界清除程序。在这一应用中,我们使用原图像作为模板,并使用下面的标记图像:

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_14

边界清除算法首先计算形态学重建(简单的提取接触边界的目标),然后计算下面的差

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_二值图像_15

得到一幅其中目标不接触边界的图像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;

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_16

2 灰度级形态学重建

2.1 测地膨胀和腐蚀

假设f和g是相同大小的灰度图像,且f≤g,即f在图像中任一点的灰度比g在这一点的灰度小。f相对于g的大小为1的测地膨胀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_二值图像_17

其中,^表示逐点最小算子,b是一个合适的结构元。我们看到,大小为1的测地膨胀的获得方式如下:首先计算b对f的膨胀,然后选择(x,y)处膨胀结果和g之间的最小者。f相对于g的大小为n的测地膨胀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_18

类似的,f相当于g的大小为1的测地腐蚀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_19

其中,v表示逐点最大算子f相对于g的大小为n的测地腐蚀定义为

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_20

灰度图像重建开运算:

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_迭代_21

先腐蚀输入图像,然后将腐蚀后的图像作为标记图像,将图像本身作为模板,进行膨胀,反复迭代至稳定。

重建闭运算:

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_22

先膨胀再腐蚀,最后求图像的补集。

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;
}

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_二值图像_23

比较灰度形态学开运算结果与重建开运算结果Matable 灰度级形态学重建_灰度_24

 (和参考文献一样,按照书上的步骤效果一般)

 

参考:

1. 冈萨雷斯《数字图像处理(第四版)》Chapter 9 (所有图片可在链接中下载)

2. 《数字图像处理(第四版)》阅读随笔 ch9(形态学重建、灰度级形态学)

3. 【youcans 的 OpenCV 例程200篇】131. 形态学重建之竖线字符提取

4. 【youcans 的 OpenCV 例程200篇】146. 基于灰度形态学的复杂背景图像重建