膨胀与腐蚀及trackbar滑动条
膨胀与腐蚀
膨胀和腐蚀的主要用途:
- 消除噪声;
- 分割出独立的图像元素,在图像中连接相邻的元素;
- 寻找图像中明显的极大值或极小值区;
- 求出图像的梯度;
- 参考链接:https://blog.csdn.net/qq_40855366/article/details/81177174
https://jingyan.baidu.com/article/f96699bbf99d9e894f3c1b4c.html
膨胀
转载于https://blog.csdn.net/qq_40855366/article/details/81177174
- 膨胀就是求局部最大值的操作。
- 按数学方面来说,膨胀或者腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行卷积。
- 核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点(anchorpoint)。多数情况下,核是一个小的中间带有参考点和实心正方形或者圆盘,其实,我们可以把核视为模板或者掩码。
- 而膨胀就是求局部最大值的操作,核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长。如下图所示,这就是膨胀操作的初衷。
右图比左图大一圈
腐蚀
转载于https://jingyan.baidu.com/article/f96699bbf99d9e894f3c1b4c.html
腐蚀原理:
腐蚀:局部最小值(与膨胀相反);
①定义一个卷积核B,
核可以是任何的形状和大小,且拥有一个单独定义出来的参考点-锚点(anchorpoint);
通常和为带参考点的正方形或者圆盘,可将核称为模板或掩膜;
②将核B与图像A进行卷积,计算核B覆盖区域的像素点最小值;
③将这个最小值赋值给参考点指定的像素;
因此,图像中的高亮区域逐渐减小。
opencv中的函数讲解
- OpenCV中膨胀函数-dilate()
void dilate(
InputArray src,//输入
OutputArray dst, //输出
InputArray kernel, //核大小
Point anchor=Point(-1,-1),// 锚位置,默认值(-1,-1)为中心
int iterations=1, //迭代次数,默认值1
int borderType=BORDER_CONSTANT,//图像边界像素模式,默认值BORDER_CONSTANT
const Scalar& borderValue =morphologyDefaultBorderValue()//边界值,有默认值,一般默认即可
)
使用dilate函数,一般只需要填前面的三个参数,后面的四个参数都有默认值,而且往往会结合getStructuringElement()
- OpenCV中腐蚀函数-erode()
void erode(
InputArray src,//输入
OutputArray dst, //输出
InputArray kernel, //核大小,当为NULL时,表示使用参考点位于中心3*3的核。一般使用函数getStructuringElement()配合使用
Point anchor=Point(-1,-1),// 锚位置,(-1,-1)为中心
int iterations=1, //迭代次数
int borderType=BORDER_CONSTANT,//图像边界像素模式
const Scalar& borderValue=morphologyDefaultBorderValue()//边界值
)
erode函数与膨胀函数类似前三个参数需要填写,后面的四个参数有默认值可根据需要填写。
- 结构元素
getStructuringElement(int shape, Size ksize, Point anchor=Point(-1,-1));
参数:
shape:表核的形状,矩形MORPH_RECT;交叉形MORPH_CROSS;椭圆形MORPH_ELLIPSE;
ksize:核尺寸大小;
anchor:锚点的位置,锚点只影响形态学运算结果的偏移;
功能:返回指定形状和尺寸的结构元素;
注意膨胀与腐蚀的对象
- 此处需要明确图片中的前景像素和背景像素对应像素值,或者说要明确图片中的像素值较大的部分,因为膨胀、腐蚀的操作对象是相对于像素值较大的部分而言的,即高亮白部分而不是黑色部分,要注意下面几点。
- 膨胀是图像中的高亮部分进行膨胀,领域扩张,效果图拥有比原图更大的高亮区域;
- 腐蚀是图像中的高亮部分被腐蚀掉,领域缩减,效果图拥有比原图更小的高亮区域;
- 膨胀
- 如下图所示对左图进行膨胀操作时,会发现白色背景区域变大,黑色区域的字变小,因为膨胀操作对应的是像素值较高的部分如白色区域,所以白色区域膨胀了,黑色区域缩小了。
- 而对下图白色为前景黑色为背景的图进行膨胀时,结果如下,同样是白色的区域变大了,黑色的区域变小了。但前景的白色字变大了,而不是像上图中的黑色字变小了
- 对左侧图像进行腐蚀操作结果如下,像素值高的部分白色区域变小,而像素值低的部分黑色区域变大
创建滑动条createTrackbar
转载于:https://www.cnblogs.com/geek-hao/p/11668248.html
此链接关于createTrackbar的用法很详尽,尤其是代码部分
- 函数原型:
CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,int* value, int count,TrackbarCallback onChange = 0, void* userdata = 0);
参数1:trackbarname,这个参数用来给这个滚动条取一个名字;
参数2:winname,这个参数用来指定你要吧这个滚动条用到那个窗口上;
参数3:value,这个参数用来设置滑块初始值位置,同时记录滑块以后的位置;
参数4:count,这个参数用来指定滚动条可以滚动的最大值;
参数5:onChange,这个参数可以理解为一个函数类型的变量(当然这样说感觉有点怪),用来接收回调函数函数名的,默认值为0;
参数6:userdata,这个变量这个参数是用户传给回调函数的数据,用来处理轨迹条事件,默认值为0。
- 回调函数的函数原型:
void (TrackbarCallback)(int pos, void userdata);
- 有些代码中定义回调函数时为什么必须要有上面俩参数。回调函数是一个必须依托于createTrackbar()函数而使用的函数,他不能单独拿来使用。
- 第一个形参pos,它表示的是当前滑块所在的位置,它的值是createTrackbar()传给他的,也就是createTrackbar()形参value的值,这个传输过程是在createTrackbar()内部实现的,无需深究。
- 然后回调函数形参userdata的值就是通过createTrackbar()的形参userdata直接得到的,所以createTrackbar()的形参userdata其实就是专门给回调函数准备的。可以传递各种类型数据如Mat,但在回调函数使用时需要转换类型如下
示例:
//TrackBar发生改变的回调函数
void onChangeTrackBar(int pos, void* userdata);
//主函数
int main()
{
//trackbar的值
int posTrackBar = 0;
//trackbar的最大值
int maxValue = 255;
//读入图像,以灰度图形式读入
Mat img = imread("F:\\图片\\timg.jpg", 0);
//新建窗口
namedWindow("二值化");
imshow("二值化", img);
//创建trackbar,我们把img作为数据传进回调函数中
createTrackbar("pos", "二值化", &posTrackBar, maxValue, onChangeTrackBar, &img);
waitKey();
return 0;
}
// 回调函数
void onChangeTrackBar(int posTrackBar, void* img)
{
// 强制类型转换
Mat src = *(Mat*)(img);
Mat dst;
// 二值化
threshold(src, dst, posTrackBar, 255, 0);
imshow("二值化", dst);
}
运行效果
- 如下图所示,当膨胀的值为1时,实际在回调函数中的卷积核的大小为1*2+1即Size(3,3)大小的,因为卷积核必须是奇数,而滑动条则是由0到定义最大值的整数,所以要将其在回调函数中转化一下,这样在滑动滑块的时候可以看到一系列的变化,有利于对膨胀的理解。此处膨胀后,白色像素值大的区域变大,而黑色像素值为0的区域就变小了。
下面图片的背景及前景颜色与上方图片相反
- 同理对图片进行腐蚀操作
-
前景为黑色,背景为黑色时
-
前景为白色,背景为黑色时
代码实现
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
Mat src, dst;
char OUTPUT_WIN[] = "output image";//定义一个输出窗口名
//int element_size = 3;//此处定义滑动条当前值,若此处使用全局变量,就无需在回调函数中定义形参,回调函数可定义为void CallBack_dilate(int , void* );
//int max_size = 21;//定义滑动条最大值
void CallBack_dilate(int pos, void* userdata); //声明膨胀的回调函数
void CallBack_erode(int pos, void* userdata); //声明腐蚀的回调函数
int main(){
src = imread("G:/OpenCV/opencv笔记所用图片/wen2.png");
if (src.empty())
{
cout << "could not load image..."<< endl;
getchar();
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);//命名一个窗口显示输入图像
imshow("input image", src);
int element_size = 3;//此处定义滑动条当前值
int max_size = 21;//定义滑动条最大值
Mat strElement;
namedWindow(OUTPUT_WIN, CV_WINDOW_AUTOSIZE);//命名一个窗口显示输出图像
//创建膨胀滑动条
createTrackbar("膨胀 :", OUTPUT_WIN, &element_size, max_size, CallBack_dilate);//滑动条名称,//窗口名称,//滑动条当前值,//滑动条最大值,//回调函数,//最后的用户参数默认不写(也可以传递各种类型数据如Mat,但在回调函数使用时需要转换类型)
//创建腐蚀滑动条
createTrackbar("腐蚀 :", OUTPUT_WIN, &element_size, max_size, CallBack_erode);
waitKey(0);
return 0;
}
//定义膨胀的回调函数
void CallBack_dilate(int element_size, void* strElement) {
int s = element_size * 2 + 1; //此处将createTrackbar传递过来的element_size值变为奇数
//此处s必须为奇数,因为后面获取结构元素中的Size(s, s)必须是奇数
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));//此处定义的结构元素为方形,大小长s,宽s,锚点为中心
dilate(src, dst, structureElement, Point(-1, -1), 1);//膨胀函数,对原始图像进行膨胀,核的大小为structureElement,通过调节核的大小来调节膨胀程度的大小
//erode(src, dst, structureElement);
imshow(OUTPUT_WIN, dst);
return;
}
//定义腐蚀的回调函数
void CallBack_erode(int element_size, void* userdata) {
int s = element_size * 2 + 1;
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));//此处定义的结构元素为方形,大小长s,宽s,锚点为中心
// dilate(src, dst, structureElement, Point(-1, -1), 1);
erode(src, dst, structureElement);//腐蚀函数,对原始图像进行腐蚀操作,此处后4个参数采用默认值
imshow(OUTPUT_WIN, dst);
return;
}