0


【OpenCv • c++】基础边缘检测算子 —— Sobel

  • 🚀 个人简介:CSDN「博客新星」TOP 10 , C/C++ 领域新星创作者
  • 💟 作 者:锡兰_CC ❣️
  • 📝 专 栏:【OpenCV • c++】计算机视觉
  • 🌈 若有帮助,还请关注➕点赞➕收藏,不行的话我再努努力💪💪💪

文章目录

什么是边缘检测

边缘检测是图像处理与计算机视觉中最重要的技术之一,其目的是检测识别出图像中亮度变化剧烈的像素点构成的集合。图像边缘的正确检测对于分析图像中的内容、实现图像中物体的分割、定位等具有重要的作用。边缘检测大大减少了源图像的数据量,剔除了与目标不相干的信息,保留了图像重要的结构属性。

边缘检测算子是利用图像边缘的突变性质来检测边缘的,通常情况下边缘检测有以下三种类型。

  • 一阶微分:以一阶微分为基础的边缘检测,通过计算图像的梯度值来检测图像边缘,如Sobel算子,Prewitt算子,Roberts算子及差分边缘检测。
  • 二阶微分:以二阶微分为基础的边缘检测,通过寻求二阶导数中的过零点来检测边缘,如拉普拉斯算子,高拉普拉斯算子,Canny算子边缘检测。
  • 混合一阶微分和二阶微分:以混合一阶微分和二阶微分为基础的边缘检测,综合利用一阶微分和二阶微分的特征,如Marr-Hildreth边缘检测算子。

什么是 Sobel 算子

Q:****什么是Sobel 算子?
A: Sobel 算子常用于图像识别中的边缘检测,计算图像灰度函数的近似梯度。Sobel 算子在进行边缘检测时效率很高,当对精度要求不是很高的时候,可以酌情考虑使用。

非极大值抑制 Sobel 检测

非极大值抑制 Sobel 的边缘检测实现步骤如下:

  • 将图像转为 32 位浮点型数据,定义水平或垂直方向的 Sobel 算子。
  • 使用 filter2D 完成图像与算子的卷积操作,计算卷积结果的梯度幅值。
  • 自适应计算出梯度幅值阈值,阈值设置不梯度幅值的均乘以 4,根据阈值对水平或垂直的领域区梯度进行比较。
  • 判断当前邻域梯度是否大于水平或垂直邻域梯度,自适应完成边缘检测出二值化图像的操作。

参考代码

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<opencv2/imgproc.hpp>#include<iostream>usingnamespace cv;usingnamespace std;boolSobelVerEdge(cv::Mat srcImage, cv::Mat& resultImage){CV_Assert(srcImage.channels()==1);
    srcImage.convertTo(srcImage, CV_32FC1);// 水平方向的 Sobel 算子
    cv::Mat sobelx =(cv::Mat_<float>(3,3)<<-0.125,0,0.125,-0.25,0,0.25,-0.125,0,0.125);
    cv::Mat ConResMat;// 卷积运算
    cv::filter2D(srcImage, ConResMat, srcImage.type(), sobelx);// 计算梯度的幅度
    cv::Mat graMagMat;
    cv::multiply(ConResMat, ConResMat, graMagMat);// 根据梯度幅度及参数设置阈值int scaleVal =4;double thresh = scaleVal * cv::mean(graMagMat).val[0];
    cv::Mat resultTempMat = cv::Mat::zeros(graMagMat.size(), graMagMat.type());float* pDataMag =(float*)graMagMat.data;float* pDataRes =(float*)resultTempMat.data;constint nRows = ConResMat.rows;constint nCols = ConResMat.cols;for(int i =1; i != nRows -1;++i){for(int j =1; j != nCols -1;++j){// 计算该点梯度与水平或垂直梯度值大小比较结果bool b1 =(pDataMag[i * nCols + j]> pDataMag[i *
                nCols + j -1]);bool b2 =(pDataMag[i * nCols + j]> pDataMag[i *
                nCols + j +1]);bool b3 =(pDataMag[i * nCols + j]> pDataMag[(i -1)* nCols + j]);bool b4 =(pDataMag[i * nCols + j]> pDataMag[(i +1)* nCols + j]);// 判断邻域梯度是否满足大于水平或垂直梯度// 并根据自适应阈值参数进行二值化
            pDataRes[i * nCols + j]=255*((pDataMag[i * nCols + j]> thresh)&&((b1 && b2)||(b3 && b4)));}}
    resultTempMat.convertTo(resultTempMat, CV_8UC1);
    resultImage = resultTempMat.clone();returntrue;}intmain(){
    cv::Mat srcImage = cv::imread("cc.png",0);
    cv::Mat resultImage;if(!srcImage.data)return-1;
    cv::imshow("srcImage", srcImage);//非极大值抑制细化数值sobel检测SobelVerEdge(srcImage, resultImage);
    cv::namedWindow("非极大值抑制", cv::WINDOW_FREERATIO);
    cv::imshow("非极大值抑制", resultImage);
    cv::waitKey(0);return0;}

实现效果

在这里插入图片描述

图像直接卷积实现 Sobel

图像直接卷积 Sobel 边缘检测实现比较简单,首先定义水平或垂直方向的 Sobel 核因子,直接对源图像进行窗遍历,计算窗内的领域梯度幅值,然后根据梯度模场进行二值化操作,完成图像的水平或垂直方向的边缘检测。

参考代码

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<opencv2/imgproc.hpp>#include<iostream>usingnamespace cv;usingnamespace std;//图像直接卷积实现SobelboolsobelEdge(const cv::Mat image, cv::Mat &result, uchar threshold){CV_Assert(image.channels()==1);//初始化水平核因子
    cv::Mat sobelx =(cv::Mat_<float>(3,3)<<1,0,-1,2,0,-2,1,0,-1);//初始化垂直核因子
    cv::Mat sobely =(cv::Mat_<float>(3,3)<<1,2,1,0,0,0,-1,-2,-1);
    result = cv::Mat::zeros(image.rows -2, image.cols -2, image.type());double graMag =0;for(int i =1; i < image.rows -1; i++){for(int j =1; j < image.cols -1; j++){float edgex =0, edgey =0;//遍历计算水平与垂直梯度for(int k =-1; k <2; k++){for(int p =-1; p <2; p++){
                    edgex +=(float)image.at<uchar>(k + i, p + j)* sobelx.at<float>(1+ k,1+ p);
                    edgey +=(float)image.at<uchar>(k + i, p + j)* sobely.at<float>(1+ k,1+ p);}}//计算梯度模长
            graMag =sqrt(pow(edgex,2)+pow(edgey,2));//二值化
            result.at<uchar>(i -1, j -1)=((graMag > threshold)?255:0);}}returntrue;}intmain(){
    cv:: Mat srcImage = cv::imread("cc.png",0);
    cv::Mat resultImage;if(!srcImage.data)return-1;
    cv::imshow("srcImage", srcImage);//图像直接卷积实现 sobel 检测sobelEdge(srcImage, resultImage,100);
    cv::imshow("图像直接卷积", resultImage);
    cv::waitKey(0);return0;}

实现效果

在这里插入图片描述

图像卷积下非极大值抑制 Sobel

图像卷积下非极大值抑制 Sobel 与 非极大值抑制 Sobel 检测实现方法类似,能够较好地剔除虚假边缘点。

参考代码

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<opencv2/imgproc.hpp>#include<iostream>usingnamespace cv;usingnamespace std;boolsobelOptaEdge(const cv::Mat& srcImage, cv::Mat& resultImage,int flag){CV_Assert(srcImage.channels()==1);// 初始化sobel水平核因子
    cv::Mat sobelX =(cv::Mat_<double>(3,3)<<1,0,-1,2,0,-2,1,0,-1);// 初始化sebel垂直核因子
    cv::Mat sobelY =(cv::Mat_<double>(3,3)<<1,2,1,0,0,0,-1,-2,-1);// 计算水平与垂直卷积
    cv::Mat edgeX, edgeY;filter2D(srcImage, edgeX, CV_32F, sobelX);filter2D(srcImage, edgeY, CV_32F, sobelY);// 根据传入参数确定计算水平或垂直边缘int paraX =0;int paraY =0;switch(flag){case0: paraX =1;
        paraY =0;break;case1:  paraX =0;
        paraY =1;break;case2:  paraX =1;
        paraY =1;break;default:break;}
    edgeX =abs(edgeX);
    edgeY =abs(edgeY);
    cv::Mat graMagMat = paraX * edgeX.mul(edgeX)+ paraY * edgeY.mul(edgeY);// 计算阈值 int scaleVal =4;double thresh = scaleVal * cv::mean(graMagMat).val[0];
    resultImage = cv::Mat::zeros(srcImage.size(), srcImage.type());for(int i =1; i < srcImage.rows -1; i++){float* pDataEdgeX = edgeX.ptr<float>(i);float* pDataEdgeY = edgeY.ptr<float>(i);float* pDataGraMag = graMagMat.ptr<float>(i);// 阈值化和极大值抑制for(int j =1; j < srcImage.cols -1; j++){if(pDataGraMag[j]> thresh &&((pDataEdgeX[j]> paraX * pDataEdgeY[j]&& pDataGraMag[j]>
                    pDataGraMag[j -1]&& pDataGraMag[j]> pDataGraMag[j +1])||(pDataEdgeY[j]> paraY * pDataEdgeX[j]&& pDataGraMag[j]>
                        pDataGraMag[j -1]&& pDataGraMag[j]> pDataGraMag[j +1])))
                resultImage.at<uchar>(i, j)=255;}}returntrue;}intmain(){
    cv::Mat srcImage = cv::imread("cc.png",0);
    cv::Mat resultImage;if(!srcImage.data)return-1;
    cv::imshow("srcImage", srcImage);//非极大值抑制细化数值sobel检测sobelOptaEdge(srcImage, resultImage,2);
    cv::imshow("非极大值抑制", resultImage);
    cv::waitKey(0);return0;}

实现效果

在这里插入图片描述

OpenCV 库调用 Sobel 函数

在 OpenCV 中提供了 Sobel 函数来计算图像边缘,详细如下:

voidsobel(InputArray src, 
    OutputArray dst,int ddepth,int dx,int dy,int ksize =3,double scale =1,double delta =0,int borderType = BORDER_DEFAULT)

其中,

ddepth

代表输出图像的深度,

dx

x

方向的导数运算参数,

dy

y

方向导数运算参数,

ksize

为 Sobel 内核的大小,设置为奇数,默认参数为3,

scale

为可选的缩放导数的比例常数,

data

为可选的增量常数,被叠加到导数中,

borderType

用于判断图像边界的模式。

参考代码

#include<opencv2/core/core.hpp>#include<opencv2/highgui/highgui.hpp>#include<opencv2/imgproc.hpp>#include<iostream>usingnamespace cv;usingnamespace std;intmain(){
    cv::Mat srcImage = cv::imread("cc.png",0);
    cv::Mat edgeXMat, edgeYMat, resultImage;if(!srcImage.data)return-1;//X 方向 Sobel 边缘Sobel(srcImage, edgeXMat, CV_16S,1,0,3,1,0, BORDER_DEFAULT);convertScaleAbs(edgeXMat, edgeXMat);//Y 方向 Sobel 边缘Sobel(srcImage, edgeYMat, CV_16S,0,1,3,1,0, BORDER_DEFAULT);convertScaleAbs(edgeYMat, edgeYMat);//整合
    resultImage = edgeXMat + edgeYMat;//显示图像
    cv::imshow("srcImage", srcImage);
    cv::imshow("X", edgeXMat);
    cv::imshow("Y", edgeYMat);
    cv::imshow("直接调用 Sobel 库", resultImage);
    cv::waitKey(0);return0;}

实现效果

在这里插入图片描述

其他

更多专栏订阅:

  • 👍 【开卷数据结构】
  • 💛 【备战蓝桥,冲击省一】
  • 💕 从零开始的 c++ 之旅
  • 💖 【OpenCV • c++】计算机视觉

本文转载自: https://blog.csdn.net/Ceylan__/article/details/130473918
版权归原作者 锡兰_CC 所有, 如有侵权,请联系我们删除。

“【OpenCv • c++】基础边缘检测算子 —— Sobel”的评论:

还没有评论