前言:
本文的主角是OpenCV的morphologyEX函数,它利用基本的膨胀和腐蚀技术,来实现更加高级的形态学变换,如开闭运算、形态学梯度、“顶帽”和“黑帽”等。
首先,我们需要知道,形态学的高级形态,往往都是建立在腐蚀和膨胀这两个基本操作之上的。而关于腐蚀和膨胀的概念和演示代码请参考上一节。有了腐蚀和膨胀的基础,再来学习高级形态学操作就不难了。
一、morphologyEX函数
OpenCV中提供了一个morphologyEX函数来实现各种高级的形态学处理,其函数声明如下:
其参数解释如下:
MORPH_OPEN – 开运算(Opening operation)
MORPH_CLOSE – 闭运算(Closing operation)
MORPH_GRADIENT -形态学梯度(Morphological gradient)
MORPH_TOPHAT - “顶帽”(“Top hat”)
MORPH_BLACKHAT - “黑帽”(“Black hat")
注意:另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。
接下来编写演示代码。
二、开操作
#include
#include
#include
using namespace cv;
using namespace std;
Mat src, dst; //定义输入图像和输出图像(两个全局变量)
Mat kernel; // 定义结构元
int r=1; //定义结构元半径
int r_max=20; //定义结构元最大半径
string window = "开操作";
void callBack_func(int, void*);
int main( )
{
src = imread("test9.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow(window, CV_WINDOW_AUTOSIZE);
createTrackbar("半径", window, &r,r_max, callBack_func);
callBack_func(0,0);
waitKey(0);
return 0;
}
void callBack_func(int ,void *)
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1)); //创建结构元
morphologyEx(src, dst, MORPH_OPEN, kernel, Point(-1, -1), 1); // 形态学处理--开运算
imshow(window,dst); // 显示形态学处理后的效果
}
运行程序,我们先看一下原图:
然后是作开运算后的效果:
我们拖动TrackBar,看看不同结构元半径对开运算的影响效果:
从图中我们可以很明显的看出,当结构元半径调整到4时,白色小方块消失了,而大矩形方块的面积似乎变化不大。因此,我们可以利用先腐蚀后膨胀的开运算过程,来消除小物体,在平滑较大物体的边界的同时不明显改变其面积。
三、闭运算
#include
#include
#include
using namespace cv;
using namespace std;
Mat src, dst; //定义输入图像和输出图像(两个全局变量)
Mat kernel; // 定义结构元
int r=1; //定义结构元半径
int r_max=20; //定义结构元最大半径
string window = "开操作";
void callBack_func(int, void*);
int main( )
{
src = imread("test10.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow(window, CV_WINDOW_AUTOSIZE);
createTrackbar("半径", window, &r,r_max, callBack_func);
callBack_func(0,0);
waitKey(0);
return 0;
}
void callBack_func(int ,void *)
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1)); //创建结构元
morphologyEx(src, dst, MORPH_CLOSE, kernel, Point(-1, -1), 1); // 形态学处理--闭运算
imshow(window,dst); // 显示形态学处理后的效果
}
运行程序,我们先看下原图:
然后再看不同结构元半径下,闭操作对图像的影响:
四、形态学梯度
形态学梯度就是指膨胀结果减去腐蚀结果。因为膨胀是取领域内的最大值,从而增大亮度高的区域的面积(在二值图中,就是增大白色区域的面积);而腐蚀是取邻域内的最小值,从而减小亮度高的区域的面积,所以,形态学梯度得到的就是图像中物体的边界。
接下来我们来编写演示代码:
#include
#include
#include
using namespace cv;
using namespace std;
Mat src, dst; //定义输入图像和输出图像(两个全局变量)
Mat kernel; // 定义结构元
int r=1; //定义结构元半径
int r_max=20; //定义结构元最大半径
string window = "开操作";
void callBack_func(int, void*);
int main( )
{
src = imread("test9.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow(window, CV_WINDOW_AUTOSIZE);
createTrackbar("半径", window, &r,r_max, callBack_func);
callBack_func(0,0);
waitKey(0);
return 0;
}
void callBack_func(int ,void *)
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1)); //创建结构元
morphologyEx(src, dst, MORPH_GRADIENT, kernel, Point(-1, -1), 1); // 形态学处理--形态学梯度
imshow(window,dst); // 显示形态学处理后的效果
}
运行程序,我们先看下原图:
然后再看下不同结构元半径下的形态学梯度处理后的效果:
显然,在图像经过阈值处理后,我们可以用形态学梯度来提取物体边缘部分。
五、顶帽
顶帽变换的定义是原图像减去开运算结果,开运算可以消除暗背景下的较亮区域,那么如果用原图减去开运算结果就可以得到原图中灰度较亮的区域,所以又称白顶帽变换。
我们编写演示代码如下:
#include
using namespace cv;
using namespace std;
Mat src, dst,dst2; //定义输入图像和输出图像(三个全局变量)
Mat kernel; // 定义结构元
int r=1; //定义结构元半径
int r_max=20; //定义结构元最大半径
string window = "开运算";
void callBack_func(int, void*);
int main( )
{
src = imread("test9.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow(window, CV_WINDOW_AUTOSIZE);
createTrackbar("半径", window, &r,r_max, callBack_func);
callBack_func(0,0);
waitKey(0);
return 0;
}
void callBack_func(int ,void *)
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1)); //创建结构元
morphologyEx(src, dst, MORPH_OPEN, kernel, Point(-1, -1), 1); // 形态学处理--开运算
imshow(window,dst); // 显示形态学处理后的效果
morphologyEx(src, dst2, MORPH_TOPHAT, kernel, Point(-1, -1), 1); // 形态学处理--开帽变换
imshow("顶帽", dst2); // 显示开帽变换后的效果
}
运行程序,当结构元半径为1时:
当结构元半径为3时:
当结构元半径为4时:
六、底帽变换
底帽变换的定义是图像减去闭运算结果,闭运算可以删除亮度较高背景下的较暗区域,那么用原图减去闭运算结果就可以得到原图像中灰度较暗的区域,所以又称黑底帽变换。
我们编写演示代码:
#include
#include
#include
using namespace cv;
using namespace std;
Mat src, dst,dst2; //定义输入图像和输出图像(三个全局变量)
Mat kernel; // 定义结构元
int r=1; //定义结构元半径
int r_max=20; //定义结构元最大半径
string window = "闭运算";
void callBack_func(int, void*);
int main( )
{
src = imread("test10.png");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
namedWindow("原图:", CV_WINDOW_AUTOSIZE);
imshow("原图:", src);
namedWindow(window, CV_WINDOW_AUTOSIZE);
createTrackbar("半径", window, &r,r_max, callBack_func);
callBack_func(0,0);
waitKey(0);
return 0;
}
void callBack_func(int ,void *)
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(2*r+1, 2*r+1)); //创建结构元
morphologyEx(src, dst, MORPH_CLOSE, kernel, Point(-1, -1), 1); // 形态学处理--闭运算
imshow(window,dst); // 显示形态学处理后的效果
morphologyEx(src, dst2, MORPH_BLACKHAT, kernel, Point(-1, -1), 1); // 形态学处理--底帽变换
imshow("底帽", dst2); // 显示底帽变换后的效果
}
运行程序,我们可以看到:
改变结构元的半径:
显然那个小洞被找出来了。
七、总结
形态学处理往往是在阈值处理以后才进行的,因为当图像被转化为二值图以后,这个时候只有黑白两者颜色,然后再进行形态学处理是效果最好的。