1. 膨胀和腐蚀
首先需要注意,腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
膨胀与腐蚀能实现多种多样的功能,主要如下:
- 消除噪声
- 分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素。
- 寻找图像中的明显的极大值区域或极小值区域
- 求出图像的梯度
1.1. 膨胀
膨胀就是求局部最大值的操作,核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长。
void dilate(InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()
)
参数列表:
- InputArray src:输入图像,图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或CV_64F其中之一。
- OutputArray dst:目标图像,需要和源图片有一样的尺寸和类型。
- InputArray kernel:膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数getStructuringElement配合这个参数的使用,getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵getStructuringElement函数的参数为内核的形状,尺寸以及锚点的位置。
使用dilate函数,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。
完整代码:
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//膨胀
int main()
{
Mat img = imread("../logo2.jpg");
namedWindow("original", WINDOW_NORMAL);
imshow("original", img);
Mat out;
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//膨胀操作
dilate(img, out, element);
namedWindow("膨胀操作", WINDOW_NORMAL);
imshow("膨胀操作", out);
imwrite("dilate.jpg",out);
waitKey();
}
效果展示:
2.2 腐蚀
膨胀和腐蚀是相反的一对操作,所以腐蚀就是求局部最小值的操作。
erode函数,使用像素邻域内的局部极小运算符来腐蚀一张图片,从src输入,由dst输出。支持就地(in-place)操作。
void erode(
InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()
)
参数列表类比dilate函数,不赘述。
完整代码:
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//腐蚀
int main()
{
Mat img = imread("../logo2.jpg");
namedWindow("original", WINDOW_NORMAL);
imshow("original", img);
Mat out;
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//腐蚀操作
erode(img, out, element);
namedWindow("腐蚀操作", WINDOW_NORMAL);
imshow("腐蚀操作", out);
imwrite("erode.jpg",out);
waitKey();
}
效果如下:
2.开运算、闭运算、形态学梯度、顶帽、黑帽
2.1 开运算
开运算(Opening Operation),其实就是先腐蚀后膨胀的过程,可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。
2.2 闭运算
先膨胀后腐蚀的过程称为闭运算(Closing Operation),能够排除小型黑洞(黑色区域)。
2.3 形态学梯度
形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来保留物体的边缘轮廓。
2.4 顶帽
顶帽运算(Top Hat)又常常被译为”礼帽“运算。为原图像与上文刚刚介绍的“开运算“的结果图之差。
因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。
顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
2.5 黑帽
黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。
所以,黑帽运算用来分离比邻近点暗一些的斑块。
完整代码如下所示:
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
//高级形态学处理
int main()
{
Mat im = imread("../logo2.jpg");
Mat img=im.clone();
namedWindow("original", WINDOW_NORMAL);
imshow("original", img);
Mat out;
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//高级形态学处理,调用这个函数就可以了,具体要选择哪种操作,就修改第三个参数就可以了。这里演示的是形态学梯度处理
morphologyEx(img, out, MORPH_OPEN, element);
namedWindow("开运算", WINDOW_NORMAL);
imshow("开运算", out);
imwrite("open.jpg",out);
waitKey();
Mat img1=im.clone();
Mat out1;
morphologyEx(img1, out1, MORPH_CLOSE, element);
namedWindow("闭运算", WINDOW_NORMAL);
imshow("闭运算", out1);
imwrite("close.jpg",out1);
waitKey();
Mat img2=im.clone();
Mat out2;
morphologyEx(img, out2, MORPH_GRADIENT, element);
namedWindow("形态学梯度", WINDOW_NORMAL);
imshow("形态学梯度", out2);
imwrite("gradient.jpg",out2);
waitKey();
Mat img3=im.clone();
Mat out3;
morphologyEx(img3, out3, MORPH_TOPHAT, element);
namedWindow("顶帽", WINDOW_NORMAL);
imshow("顶帽", out3);
imwrite("tophat.jpg",out3);
waitKey();
Mat img4=im.clone();
Mat out4;
morphologyEx(img4, out4, MORPH_BLACKHAT, element);
namedWindow("黑帽", WINDOW_NORMAL);
imshow("黑帽", out4);
imwrite("blackhat.jpg",out4);
waitKey();
}