形态学处理

在传统图像处理中,形态学处理可算是最为强大的功能之一,只要你稍微有一些粗略的识别-分类需求需要对图像进行前处理,形态学都能帮上大忙,而且具备极高的计算效率。

这得益于其计算原理的简单,腐蚀膨胀是两种基本操作,注意这里形态学处理的对象都是二值图

腐蚀与膨胀

腐蚀的基本原理是以二值卷积核和二值图卷积,只要一个位置的相乘结果为0,那么中心像素即为0

腐蚀基本运算

腐蚀函数原型是:

1
2
3
4
5
6
7
8
9
void erode(
cv::InputArray src, //输入二值图像
cv::OutputArray dst, //输出二值图像
cv::InputArray kernel, //卷积核
cv::Point anchor = cv::Point(-1, -1), //处理锚点,默认中心点
int iterations = 1, //腐蚀次数
int borderType = 0, //边值处理:默认常数填充
const cv::Scalar &borderValue = morphologyDefaultBorderValue() //仅borderType = 0生效, 填充常数,默认为Scalar(0)
)
通过这种运算,能够有效去除白色的无效结构,可以是白色的噪点,也可以是白色的连接结构,例如一些蜂窝填充填充、桥接结构;

膨胀是腐蚀的逆运算,其计算与腐蚀完全相反,即二值卷积核和二值图进行卷积,只要其中一个位置像素为1,那么中心像素就赋值为1:

膨胀基本运算

所以其效果是和腐蚀相反的,往往用于填充黑色结构,例如补足物体中间的黑色小洞,其函数原型是:

1
2
3
4
5
6
7
8
9
void dilate(       //参数意义同erode,不赘述
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray kernel,
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1,
int borderType = 0,
const cv::Scalar &borderValue = morphologyDefaultBorderValue()
)
其中腐蚀膨胀都需要用到卷积核结构,OpenCV通过cv::getStructuringElement提供了矩形椭圆(圆)十字形三种常用的结构,适用于不同的目标(例如圆形目标使用椭圆能维持光滑性):
1
2
3
4
5
6
7
8
9
10
cv::Mat getStructuringElement(
int shape,
cv::Size ksize,
cv::Point anchor = cv::Point(-1, -1)
)

其中shape类型:
- cv::MORPH_ELLIPSE:椭圆卷积核
- cv::MORPH_RECT:矩形卷积核
- cv::MORPH_CROSS:十字卷积核

示例:

1
2
3
4
5
6
7
8
Mat rawPic = imread("D:/Documents/Desktop/holeTest/test.png",0);
Mat binary;
cv::threshold(rawPic,binary,0,255,cv::THRESH_BINARY|cv::THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(17, 17));
Mat eroded;
erode(binary, eroded, kernel);
Mat dilated;
dilate(eroded, dilated ,kernel);

开运算与闭运算

单独的腐蚀或者膨胀都会对图像产生有害操作,要么目标腐蚀后黑色像素变多,白色结构减少;要么目标膨胀后白色像素变多,白色结构外扩;然而,腐蚀和膨胀都是逆运算,意味着当使用同一个卷积核进行膨胀和腐蚀,不会对目标像素产生损害,而仅仅去除冗余的黑色结构或者白色结构;

先腐蚀后膨胀称为开运算,去除白色结构:

1
2
3
cv::Mat opened;
Mat kernel1 = 5*getStructuringElement(cv::MORPH_ELLIPSE,Size(26,26));
morphologyEx(dilated,opened,cv::MORPH_OPEN,kernel1);

先膨胀再腐蚀称为闭运算,填充黑色结构:

1
2
3
cv::Mat opened;
Mat kernel1 = 5*getStructuringElement(cv::MORPH_ELLIPSE,Size(26,26));
morphologyEx(dilated,opened,cv::MORPH_OPEN,kernel1);

其余处理

它们反应了图像的轮廓特征,比较少用。 ### 形态学梯度 当处理参数为cv::MORPH_GRADIENT时,所求结果为形态学梯度,它是图像开运算和闭运算的差值

1
2
3
4
5
6
Mat rawPic = imread("D:/Documents/Desktop/holeTest/S000029_P1.png",0);
cv::Mat gradient;
Mat kernel = 5*getStructuringElement(cv::MORPH_ELLIPSE,Size(10,10));
morphologyEx(rawPic,gradient,cv::MORPH_GRADIENT,kernel);
namedWindow("gradient",cv::WINDOW_NORMAL);
imshow("gradient", gradient);

黑帽

处理参数为cv::MORPH_BLACKHAT,结果是原图和闭运算图的差值,即填充掉的黑色结构

1
2
3
4
5
6
Mat rawPic = imread("D:/Documents/Desktop/holeTest/S000029_P1.png",0);
cv::Mat blackHat;
Mat kernel = 5*getStructuringElement(cv::MORPH_ELLIPSE,Size(25,25));
morphologyEx(rawPic,blackHat,cv::MORPH_BLACKHAT,kernel);
namedWindow("blackHat",cv::WINDOW_NORMAL);
imshow("blackHat", blackHat);

礼帽

处理参数为cv::MORPH_TOPHAT,结果是开运算和原图的差值,即腐蚀掉的白色结构

1
2
3
4
5
6
Mat rawPic = imread("D:/Documents/Desktop/holeTest/S000029_P1.png",0);
cv::Mat topHat;
Mat kernel = 5*getStructuringElement(cv::MORPH_ELLIPSE,Size(25,25));
morphologyEx(rawPic,topHat,cv::MORPH_TOPHAT,kernel);
namedWindow("topHat",cv::WINDOW_NORMAL);
imshow("topHat", topHat);