结构元素就是在做形态学操作的卷积核,其实更直白一点就是计算的时候的有效区域.就是决定用哪些值去做运算
函数原型:
Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1))
shape:
结构化元素的形状定义: MORPH_RECT 矩形 MORPH_CROSS 十字形 MORPH_ELLIPSE椭圆形kszie:
结构化元素的大小anchor:
锚点,默认是Point(-1,-1)意思就是中心像素,也可以自己制定#include "MyOpencv.h"
int main(void)
{
// 创建一个3*3的矩形,十字,椭圆结构元素
Mat kernelRect, kernelCross, kernelEllipse;
kernelRect = cv::getStructuringElement(cv::MORPH_RECT, Size(3, 3), cv::Point(-1, -1));
kernelCross = cv::getStructuringElement(cv::MORPH_CROSS, Size(3, 3), cv::Point(-1, -1));
kernelEllipse = cv::getStructuringElement(cv::MORPH_ELLIPSE, Size(3, 3),
cv::Point(-1, -1));
cout << "形状是3 * 3的各种结构化元素: " << endl;
cout << "矩形卷积核: = " << endl;
cout << kernelRect << endl;
cout << "十字卷积核: = " << endl;
cout << kernelCross << endl;
cout << "椭圆卷积核: = " << endl;
cout << kernelEllipse << endl;
// 创建一个5*5的矩形,十字,椭圆结构元素
kernelRect = cv::getStructuringElement(cv::MORPH_RECT, Size(5, 5),
cv::Point(-1, -1));
kernelCross = cv::getStructuringElement(cv::MORPH_CROSS, Size(5, 5), cv::Point(-1, -1));
kernelEllipse = cv::getStructuringElement(cv::MORPH_ELLIPSE, Size(5, 5),
cv::Point(-1, -1));
cout << "********************************" << endl;
cout << "形状是5 * 5的各种结构化元素: " << endl;
cout << "矩形卷积核: = " << endl;
cout << kernelRect << endl;
cout << "十字卷积核: = " << endl;
cout << kernelCross << endl;
cout << "椭圆卷积核: = " << endl;
cout << kernelEllipse << endl;
return 0;
}
腐蚀和膨胀都是使用卷积核和原图像做卷积操作,然后取卷积结果的最值,如果是腐蚀操作,就取最小值,如果是膨胀操作就是取最大值.因为原图只有两个像素值0和255,所以腐蚀操作的时候,取最小值是0,给我们的感觉就是一些白色的部分被消除掉了,膨胀相反.
腐蚀的作用:
可以用来消除小且无意义的物体.
函数原型:
void erode( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
参数解释:
src:
输入图像dst:
输出图像kernel:
结构化元素anchor:
中心位置锚点iterations:
操作次数,默认值是1boardType:
边缘填充类型boardValue:
填充值(默认即可)#include "MyOpencv.h"
int main(void)
{
Mat original = cv::imread("test_04.bmp", IMREAD_GRAYSCALE);
imshow("Original", original);
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
// 腐蚀两次
Mat dst;
for (int i = 1; i < 5; i++)
{
erode(original, dst, kernel, Point(-1, -1), i);
string imageName = "EroseTimes_";
imageName += to_string(i);
imshow(imageName, dst);
}
waitKey(0);
return 0;
}
结果:
腐蚀的次数越多,白色的部分越少.
使用的核越大,白色的部分也会越少
函数原型:
void dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
参数解释:
src:
输入图像dst:
输出图像kernel:
结构化元素anchor:
中心位置锚点iterations:
操作次数,默认值是1boardType:
边缘填充类型boardValue:
填充值(默认即可)膨胀操作的目的一般是扩充边界,比如上面的腐蚀操作,可以消除掉无用的小块,但是整个目标物体可能也被缩小了.这个时候可以使用膨胀操作,让物体的白色区域复原.
#include "MyOpencv.h"
int main(void)
{
Mat original = cv::imread("test_04.bmp", IMREAD_GRAYSCALE);
imshow("Original", original);
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
// 腐蚀两次
Mat dst;
erode(original, dst, kernel, Point(-1, -1), 2);
imshow("Erode", dst);
Mat dilateDst;
// 腐蚀之后,目标物体表小了,边瘦了,如何还原呢?可以使用膨胀操作还原
dilate(dst, dilateDst, kernel, Point(-1, -1), 2);
imshow("Dilate", dilateDst);
waitKey(0);
return 0;
}
- 先腐蚀后膨胀
- 作用: 分离物体,消除小区域. 消除噪声,去除小的干扰块,而不影响原来的图像
函数原型:
void morphologyEx( InputArray src, OutputArray dst,
int op, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
op:
操作方式,开操作是MORPH_OPEN=2
案例:
例如,这个图片,使用开操作可以消除周围的小圆孔.同时尽可能最大程度的保留原来的圆孔
#include "MyOpencv.h"
int main(void)
{
Mat original = imread("test_05.bmp", IMREAD_GRAYSCALE);
imshow("Original", original);
Mat opened;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
for (int index = 1; index < 4; index++)
{
string imageName = "ClosedTimes_";
imageName += to_string(index);
morphologyEx(original, opened,MORPH_RECT, kernel, Point(-1, -1), index);
imshow(imageName, opened);
}
waitKey(0);
return 0;
}
先膨胀后腐蚀,可以填充小对象,经常用来填充前景物体中的小洞,或者是前景物体上的小黑点.
代码:
#include "MyOpencv.h"
int main(void)
{
Mat original = imread("test_06.bmp", IMREAD_GRAYSCALE);
imshow("Original", original);
Mat kernel = getStructuringElement(MORPH_RECT, Size(4, 4), Point(-1, -1));
Mat closed;
for (int index = 1; index < 4; index++)
{
morphologyEx(original, closed, MORPH_CLOSE, kernel, Point(-1, -1), index);
imshow("ClosedTimes" + to_string(index), closed);
}
waitKey(0);
return 0;
}
形态学梯度就是膨胀减去腐蚀.
形态学梯度有称为了基本梯度,对二值图像进行形态学梯度操作可以将团块(blob)的边缘突出来,可以保留物体的边缘轮廓.
#include "MyOpencv.h"
int main(void)
{
Mat original = imread("test_07.bmp", IMREAD_GRAYSCALE);
imshow("Origianl", original);
for (int index = 1; index < 4; index++)
{
Mat gradient;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(original, gradient, MORPH_GRADIENT, kernel, Point(-1, -1), index);
imshow("GradientTimes_" + to_string(index),gradient);
}
waitKey(0);
return 0;
}
顶帽操作是原图像和开操作之间的差值图像
开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图像中减去开运算后的图,得到的效果图突出了比原型轮廓周围的区域更明亮的区域.
顶帽运算往往用来分离邻近点亮一些的斑块.当一幅图像具有大幅的背景的时候,而微小物品比较有规律的时候,可以用顶帽运算进行提取小物品
使用顶帽操作,可以将周围的这种白色的小块提取出来
#include "MyOpencv.h"
int main(void)
{
Mat original = imread("test_08.bmp", IMREAD_GRAYSCALE);
imshow("Original", original);
Mat opened, tophat;
Mat kernel = getStructuringElement(MORPH_RECT, Size(7, 7), Point(-1, -1));
for (int i = 1; i < 4; i++)
{
morphologyEx(original, opened, MORPH_OPEN, kernel, Point(-1, -1), i);
morphologyEx(original, tophat, MORPH_TOPHAT, kernel, Point(-1, -1), i);
imshow("OpenedTimes_" + to_string(i), opened);
imshow("TophatTiems_" + to_string(i), tophat);
}
waitKey(0);
return 0;
}
黑帽(Black Hat)运算为闭运算的结果与原图像之差
黑帽运算后的效果图是突出了比原图轮廓周围区域更暗的区域,用来分离比邻近点暗一些的斑块
#include "MyOpencv.h"
int main(void)
{
Mat original = imread("test_09.bmp", IMREAD_GRAYSCALE);
imshow("Original", original);
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
Mat closed, blackHat;
for (int i = 1; i < 4; i++)
{
morphologyEx(original, closed, MORPH_CLOSE, kernel, Point(-1, -1), i);
morphologyEx(original, blackHat, MORPH_BLACKHAT, kernel, Point(-1, -1), i);
imshow("ClosedTimes_" + to_string(i), closed);
imshow("BlackHatTimes_" + to_string(i), blackHat);
}
waitKey(0);
return 0;
}