若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105425374
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
OpenCV开发专栏(点击传送门)
上一篇:《OpenCV开发笔记(四十):红胖子8分钟带你深入了解Laplacian(拉普拉斯)算子边缘检测(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十二):红胖子8分钟带你深入了解标准霍夫线变换(图文并茂+浅显易懂+程序源码)》
前言
红胖子来也!!!
本篇章讲解scharrr滤波器,对比sobel算子边缘检测。
Demo
Demo图像结果位置示意图:
Scharr滤波器与Sobel算子边缘检测
概述
先把sobel算子边缘检测了解清楚,Scharr滤波器其实是Sobel的实际编程操作移植,只是加大了核矩阵的数据,其精度比Sobel算子边缘检测更高,此处我们来对比一下Sobel算子边缘检测与Scharr滤波器进行边缘检测。
关于Sobel算子边缘检测具体请查看《OpenCV开发笔记(三十九):红胖子8分钟带你深入了解sobel算子边缘检测(图文并茂+浅显易懂+程序源码)》
Sobel算子与Scharr滤波器
索贝尔算产生较好的检测效果,而且对噪声具有平滑抑制作用,但是得到的边缘较粗,且可能出现伪边缘。
*当图象比较复杂时,目标与背景就不能简单地用一个阈值来分离。这时就只有求助于边缘检测法或区域生长法. 而边缘检测法中最为常用的是Sobel算子。*
Sobel算子的优点是方法简单、处理速度快,并且所得的边缘光滑、连续。其缺点是边缘较粗,由于处理时需作两值化处理,故得到的边缘与阈值的选取也有很大的关系。
在调用Sobel()时其实是可以设置ksize为::SCHARR去实现,也可单独使用Scharr滤波器,其比Sobel算子精度更高,因此选择3x3滤波器时,还是优先选择Scharr滤波器,其速度与Sobel()速度同样快。
原理
第一步:分别在x和y两个方向求导
假设输入图像为I。
水平变化:将I与奇数大小的内核进行卷积,比如当核的大小为3时,Gx的计算结果为:
垂直变化:将I与奇数大小的内核进行卷积,比如当核的大小为3时,Gy的计算结果为:
第二步:求出近似梯度
在图像的每一点,结合Gx和Gy求出近似梯度:
另外又是,也可以使用更简单的公示代替:
Scharr函数和Sobel一样,使用需要带额外的步骤
步骤一:先创建两个cv::Mat缓存
// 使用Scharr滤波器
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
步骤二:求X方向的梯度
// 步骤二:求X方向的梯度
//cv::Sobel(grayMat3Channels, gradXMat, CV_16S, 1, 0, 3, 1, 1);
cv::Scharr(grayMat3Channels, gradXMat, CV_16S, 1, 0, 1, 0,);
cv::convertScaleAbs(gradXMat, absGradXMat);
步骤三:求Y方向的梯度
// 步骤三:求Y方向的梯度
//cv::Sobel(grayMat3Channels, gradYMat, CV_16S, 0, 1, 3, 1, 1);
cv::Scharr(grayMat3Channels, gradXMat, CV_16S, 0, 1, 1, 0,);
cv::convertScaleAbs(gradYMat, absGradYMat);
步骤四:合并梯度(近似)
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
Scharr函数原型
void Scharr( InputArray src,
OutputArray dst,
int ddepth,
int dx,
int dy,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT );
- 参数一:InputArray类型的image,一般是cv::Mat;
- 参数二:OutputArray类型的dst,与输入的图像大小相同;
- 参数三:int类型的ddepth,输出图像深度,8位输入图像会导致结果被截断,组合有如下:
- 参数四:int类型的dx,x方向上的差分阶数,1或者0;
- 参数五:int类型的dy,y方向上的差分阶数,1或者0;
- 参数六:double类型的scale,缩放计算的导数值的可选比例因子;默认值为1,表示没有进行缩放;
- 参数七:double类型的detal,delta在将结果存储到dst前添加到结果中;
- 参数八:int类型的borderType,一般情况下无需关注;
颜色转换函数
void convertScaleAbs(InputArray src,
OutputArray dst,
double alpha = 1,
double beta = 0);
- 参数一:InputArray类型的image,一般是cv::Mat;
- 参数二:OutputArray类型的dst,输出图像;
- 参数三:double类型的appha;
- 参数四:double类型的depth;
(补充:其实这个就是类似于对比度和亮度的调整,但是却不是,因为当其计算结果小于0时,不会去做处理,导致值向前借位,反而变白了)
Demo源码
void OpenCVManager::testScharr()
{
QString fileName1 = "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/11.jpg";
cv::Mat srcMat = cv::imread(fileName1.toStdString());
if(!srcMat.data)
{
qDebug() << __FILE__ << __LINE__
<< "Failed to load image:" << fileName1;
return;
}
int width = 300;
int height = 200;
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat dstMat;
dstMat = cv::Mat::zeros(srcMat.size(), srcMat.type());
cv::Mat windowMat = cv::Mat(cv::Size(dstMat.cols * 3, dstMat.rows * 4),
srcMat.type());
int ksize = 1; // 核心大小
int sigmaX = 0; // x方向的标准偏差
int sigmaY = 0; // y方向的标准偏差
int ksize2 = 1;
int sigmaS = 160;
int sigmaR = 2;
cvui::window(windowMat, dstMat.cols, 0, dstMat.cols, dstMat.rows, "settings");
cv::Mat grayMat;
cv::Mat grayMat3Channels;
cv::Mat mat;
cv::cvtColor(srcMat, grayMat, CV_BGR2GRAY);
cv::cvtColor(grayMat, grayMat3Channels, CV_GRAY2BGR);
while(true)
{
windowMat = cv::Scalar(0, 0, 0);
// 原图先copy到左边
cv::Mat leftMat = windowMat(cv::Range(0, srcMat.rows),
cv::Range(0, srcMat.cols));
cv::addWeighted(leftMat, 1.0f, srcMat, 1.0f, 0.0f, leftMat);
// 中间为调整滤波参数的相关设置
cvui::printf(windowMat, width * 2 + 75, 20, "ksize = size * 2 + 1");
cvui::trackbar(windowMat, width * 2 + 75, 30, 165, &ksize, 0, 10);
cvui::printf(windowMat, width * 2 + 75, 80, "sigmaX");
cvui::trackbar(windowMat, width * 2 + 75, 90, 165, &sigmaX, 0, 100);
cvui::printf(windowMat, width * 2 + 75, 140, "sigmaY");
cvui::trackbar(windowMat, width * 2 + 75, 150, 165, &sigmaY, 0, 100);
// 复制灰度图像
{
cv::Mat rightMat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(rightMat, 0.0f, grayMat3Channels, 1.0f, 0.0f, rightMat);
}
{
// 高斯滤波
cv::Mat mat;
cv::GaussianBlur(grayMat3Channels, mat, cv::Size(ksize * 2 + 1, ksize * 2 + 1), sigmaX / 10.f, sigmaY / 10.f);
// 高斯滤波后进行边缘检测
// 使用Sobel边缘检测
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// 步骤二:求X方向的梯度
cv::Sobel(mat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
cv::convertScaleAbs(gradXMat, absGradXMat);
// 步骤三:求Y方向的梯度
cv::Sobel(mat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
cv::convertScaleAbs(gradYMat, absGradYMat);
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
cv::Mat rightMat2 = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(rightMat2, 0.0f, dstMat, 1.0f, 0.0f, rightMat2);
}
{
// 使用Sobel边缘检测
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// 步骤二:求X方向的梯度
cv::Sobel(grayMat3Channels, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
cv::convertScaleAbs(gradXMat, absGradXMat);
// 步骤三:求Y方向的梯度
cv::Sobel(grayMat3Channels, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
cv::convertScaleAbs(gradYMat, absGradYMat);
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
cvui::printf(windowMat, 75 + width * 2, height * 2 + 20, "sigmaS");
cvui::trackbar(windowMat, 75 + width * 2, height * 2 + 50, 165, &sigmaS, 101, 10000);
cvui::printf(windowMat, 75 + width * 2, height * 2 + 90, "sigmaR");
cvui::trackbar(windowMat, 75 + width * 2, height * 2 + 120, 165, &sigmaR, 1, 100);
cvui::printf(windowMat,
srcMat.cols * 2 + 75,
srcMat.rows * 1 + 100, "ksize");
cvui::trackbar(windowMat,
srcMat.cols * 2 + 75,
srcMat.rows * 1 + 130,
165,
&ksize2,
0,
3);
{
// 使用自适应流形应用高维滤波。
cv::Mat tempMat;
cv::Ptr<cv::ximgproc::AdaptiveManifoldFilter> pAdaptiveManifoldFilter
= cv::ximgproc::createAMFilter(sigmaS/100.0f, sigmaR/100.0f, true);
pAdaptiveManifoldFilter->filter(grayMat, tempMat);
cv::cvtColor(tempMat, tempMat, CV_GRAY2BGR);
// 使用Sobel边缘检测
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// 步骤二:求X方向的梯度
cv::Sobel(tempMat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
cv::convertScaleAbs(gradXMat, absGradXMat);
// 步骤三:求Y方向的梯度
cv::Sobel(tempMat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
cv::convertScaleAbs(gradYMat, absGradYMat);
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
// copy
mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
{
// 高斯滤波
cv::Mat mat;
cv::GaussianBlur(grayMat3Channels, mat, cv::Size(ksize * 2 + 1, ksize * 2 + 1), sigmaX / 10.f, sigmaY / 10.f);
// 高斯滤波后进行边缘检测
// 使用Sobel边缘检测
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// 步骤二:求X方向的梯度
// cv::Sobel(mat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
cv::Scharr(mat, gradXMat, CV_16S, 1, 0, 1, 1);
cv::convertScaleAbs(gradXMat, absGradXMat);
// 步骤三:求Y方向的梯度
// cv::Sobel(mat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
cv::Scharr(mat, gradYMat, CV_16S, 1, 0, 1, 1);
cv::convertScaleAbs(gradYMat, absGradYMat);
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
cv::Mat rightMat2 = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(rightMat2, 0.0f, dstMat, 1.0f, 0.0f, rightMat2);
}
{
// 使用Sobel边缘检测
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// 步骤二:求X方向的梯度
// cv::Sobel(grayMat3Channels, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
cv::Scharr(grayMat3Channels, gradXMat, CV_16S, 1, 0, 1, 1);
cv::convertScaleAbs(gradXMat, absGradXMat);
// 步骤三:求Y方向的梯度
// cv::Sobel(grayMat3Channels, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
cv::Scharr(grayMat3Channels, gradYMat, CV_16S, 1, 0, 1, 1);
cv::convertScaleAbs(gradYMat, absGradYMat);
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
{
// 使用自适应流形应用高维滤波。
cv::Mat tempMat;
cv::Ptr<cv::ximgproc::AdaptiveManifoldFilter> pAdaptiveManifoldFilter
= cv::ximgproc::createAMFilter(sigmaS/100.0f, sigmaR/100.0f, true);
pAdaptiveManifoldFilter->filter(grayMat, tempMat);
cv::cvtColor(tempMat, tempMat, CV_GRAY2BGR);
// 使用Sobel边缘检测
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// 步骤二:求X方向的梯度
// cv::Sobel(tempMat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
cv::Scharr(tempMat, gradXMat, CV_16S, 1, 0, 1, 1);
cv::convertScaleAbs(gradXMat, absGradXMat);
// 步骤三:求Y方向的梯度
// cv::Sobel(tempMat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
cv::Scharr(tempMat, gradYMat, CV_16S, 1, 0, 1, 1);
cv::convertScaleAbs(gradYMat, absGradYMat);
// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
// copy
mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
// 更新
cvui::update();
// 显示
cv::imshow(windowName, windowMat);
// esc键退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
.
工程模板:对应版本号v1.36.0
对应版本号v1.36.0
上一篇:《OpenCV开发笔记(四十):红胖子8分钟带你深入了解Laplacian(拉普拉斯)算子边缘检测(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十二):红胖子8分钟带你深入了解标准霍夫线变换(图文并茂+浅显易懂+程序源码)》
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105425374
本文同步分享在 博客“红胖子(AAA红模仿)”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。