OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)

目录

一、边缘检测基础理论

1、作用:

2、分类

1、基于搜索

 2、基于零穿越

3、算子比较

二、Sobel算子基础理论

1、作用

2、原理及推导

3、更详细推导

4、Sobel函数

 二、实战

1、对x方向微分

2、对y方向微分

3、线性混合

总代码

其他应用

参考资料


一、边缘检测基础理论

1、作用:

图像边缘检测大幅度地减少了数据量,并且剔除了不相关的信息保留了图像重要的结构属性

        边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的标识数字图像中亮度变化明显的点图像属性中的显著变化通常反映了属性的重要事件和变化。边缘的表现形式如下图所示:

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第1张图片纵轴:灰度值 横轴:位置信息

图像边缘检测大幅度地减少了数据量,并且剔除了不相关的信息保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类∶基于搜索基于零穿越

2、分类

基于搜索:利用一阶导数最大值检测边缘

基于零穿越:利用二阶导数为0检测边缘

1、基于搜索

基于搜索:通过寻找图像一阶导数中的最大值检测边界,然后利用计算结果估计边缘的局部方向通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法是Sobel算子Scharr算子

原理:边缘附近的像素值会有明显突变,即变化最大,也就是一阶导数最大。那么找到最大的一阶导数也就找到了像素变化最大的点,即边缘点。

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第2张图片

 2、基于零穿越

基于零穿越:通过寻找二阶导数零穿越寻找边界,代表算法是Laplace算子

原理:在一阶导数的基础上再求一次导,那么此时零点就是变化最大的点,即边缘点。

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第3张图片

3、算子比较

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第4张图片

其中,Canny算子永远的神!(最优边缘检测)

二、Sobel算子基础理论

1、作用

Sobel算子:用于边缘检测离散微分算子。(一阶)(结合了高斯平滑微分

1、边缘检测: Gx 用于检测纵向边缘, Gy 用于检测横向边缘.

2、计算法线: Gx 用于计算法线的横向偏移, Gy 用于计算法线的纵向偏移.

        Sobel边缘检测算法比较简单,实际应用中效率比canny边缘检测效率要,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选Sobel算子是高斯平滑与微分操作的结合体,所以其抗噪声能力很强,用途较多尤其是效率要求较高,而对细纹理不太关心的时候

注:需要x方向图像就对y求微分,需要y方向图像就对x求微分,两者颠倒
对x方向求导,得到的是y方向的边缘;
对y方向求导,得到的是x方向的边缘。

2、原理及推导

因为图像是二维的,所以需要在两个方向求导

垂直方向的边缘在水平方向梯度(偏导数)幅值较大

水平方向的边缘在垂直方向梯度(偏导数)幅值较大

Sobel算子刚好能描述这个图像变化

过程:

先分别求x和y方向sobel算子,再取绝对值转换成uint8),然后线性混合。(如图)

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第5张图片

梯度公式

对于图像而言,它是离散的,所以h的最小值只能是1了,那么这意味着,图像中某个像素位置的梯度(以x方向为例)等于它左右两个像素点的像素之差除以2。

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第6张图片

可以看出:后 - 前 

所以:

 OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第7张图片

 综上求出:

例:假设有一行像素是这样分布的:
123 155 173
那么,像素值为155的像素位置x方向的梯度为(173 - 123)/2 = 25

Schar算子能够弥补Sobel内核为3时的误差:(更佳的3*3滤波器Scahrr ()函数)

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第8张图片

3、更详细推导

 Prewitt算子:x方向和y方向卷积核分别为:

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第9张图片                         OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第10张图片

sobel算子:在此基础上加了一些权值,结合了高斯平滑微分求导。

Sebel算子x方向和y方向的卷积核分别为:

求出了水平方向和竖直方向的梯度GxGy之后
近似的梯度就可以用下面的方法算出来: 

 OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第11张图片

4、Sobel函数

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

函数参数解释:

InputArray src:输入的原图像,Mat类型

OutputArray dst:输出的边缘检测结果图像,Mat型,大小与原图像相同。

int ddepth:输出图像的深度,针对不同的输入图像,输出目标图像有不同的深度,具体组合如下:

- 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F

- 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F

- 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F

- 若src.depth() = CV_64F, 取ddepth = -1/CV_64F

注:ddepth =-1时,代表输出图像与输入图像相同的深度。

int dx:int类型dx,x 方向上的差分阶数,1或0

int dy:int类型dy,y 方向上的差分阶数,1或0

其中,dx=1,dy=0,表示计算X方向的导数,检测出的是垂直方向上的边缘;dx=0,dy=1,表示计算Y方向的导数,检测出的是水平方向上的边缘。

int ksize:为进行边缘检测时的模板大小为ksize*ksize,取值为1、3、5和7,其中默认值为3。特殊情况:ksize=1时,采用的模板为3*1或1*3。

当ksize=3时,Sobel内核可能产生比较明显的误差,此时,可以使用 Scharr 函数,该函数仅作用于大小为3的内核。具有跟sobel一样的速度,但结果更精确,其内核为:

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第12张图片

C++:

Sobel(gray, grad_x, CV_16S, 1,                0,           3);
    //                      x方向差分阶数    y方向差分阶数   核大小

 python:

x = cv.Sobel(img, cv.CV_16S, 1,     0)
y = cv.Sobel(img, cv.CV_16S, 0,     1)
#                 深度      x方向阶数 y方向阶数

 二、实战

首先欣赏下原图及灰度图:

src = imread("Resource/test12.jpg");
    imshow("原图", src);
    cvtColor(src, gray, COLOR_RGB2GRAY);   //转变为灰度图
    imshow("灰度图", src);

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第13张图片

1、对x方向微分

对x方向微分,得到的是y方向的边缘。 

//对x方向微分
    Sobel(gray, grad_x, CV_16S, 1,                      0,                      3);   
    //                          x方向差分阶数           y方向差分阶数           核大小
    convertScaleAbs(grad_x, abs_grad_x);     //可将任意类型的数据转化为CV_8UC1
    imshow("【边缘图x】", abs_grad_x); 
    //边缘与梯度方向垂直,所以输出的边缘是和我们所计算的某一方向的梯度是垂直的

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第14张图片

2、对y方向微分

对y方向微分,得到的是x方向的边缘。  

//对y方向微分
    Sobel(gray, grad_y, CV_16S, 0,                      1,                      3);
    //                         x方向差分阶数           y方向差分阶数           核大小
    convertScaleAbs(grad_y, abs_grad_y);     //可将任意类型的数据转化为CV_8UC1
    imshow("边缘图y", abs_grad_y);

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第15张图片

3、线性混合

//图像的线性混合
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
    imshow("线性混合", dst);

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第16张图片

总代码

C++:

//Sobel算子(微分)
#include
#include
using namespace cv;

int main()
{
    Mat src, dst, gray, grad_x, grad_y, abs_grad_x, abs_grad_y;
    src = imread("Resource/test12.jpg");
    imshow("原图", src);
    cvtColor(src, gray, COLOR_RGB2GRAY);   //转变为灰度图
    imshow("灰度图", gray);

    //对x方向微分
    Sobel(gray, grad_x, CV_16S, 1,                      0,                      3);   
    //                                          x方向差分阶数   y方向差分阶数   核大小
    convertScaleAbs(grad_x, abs_grad_x);     //可将任意类型的数据转化为CV_8UC1
    imshow("边缘图x", abs_grad_x); 
    //边缘与梯度方向垂直,所以输出的边缘是和我们所计算的某一方向的梯度是垂直的

    //对y方向微分
    Sobel(gray, grad_y, CV_16S, 0,                      1,                      3);
    //                                          x方向差分阶数   y方向差分阶数   核大小
    convertScaleAbs(grad_y, abs_grad_y);     //可将任意类型的数据转化为CV_8UC1
    imshow("边缘图y", abs_grad_y);

    //图像的线性混合
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
    imshow("线性混合", dst);
   
    waitKey(0);
}

python:

# 边缘检测(Sobel、Laplace、Canny)
import cv2 as cv


# Sobel一阶微分算子
def Sobel():
    # 1、对X和Y方向求微分
    x = cv.Sobel(img, cv.CV_16S, 1,     0)
    y = cv.Sobel(img, cv.CV_16S, 0,     1)
    #                 深度      x方向阶数 y方向阶数

    # 2、取绝对值
    absX = cv.convertScaleAbs(x)  # 转回uint8
    absY = cv.convertScaleAbs(y)

    # 3、线性混合
    dst = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
    #                          比例       比例  常数

    # 4、显示
    cv.imshow("absX", absX)
    cv.imshow("absY", absY)
    cv.imshow("dst", dst)


if __name__ == '__main__':
    # 读取图片
    img = cv.imread("Resource/test5.jpg")
    cv.imshow("img", img)

    Sobel()         #Sobel一阶微分算子

    cv.waitKey(0)

其他应用

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第17张图片

x方向梯度使竖直的黑条特征变得明显):

 OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第18张图片

OpenCV(十五)边缘检测1 -- Sobel算子(一阶微分算子,X、Y方向边缘检测)_第19张图片

参考资料

https://blog.csdn.net/weixin_44586473/article/details/93229385?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162895010516780271586263%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162895010516780271586263&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-93229385.ecpm_v1_rank_v29&utm_term=sobel&spm=1018.2226.3001.4187

https://www.baidu.com/link?url=14oBZycvBio7eVQbSDl-XWg_eyg92lbbpOCNxZu5g-rxq39fby3yvmI-_OYGULcmmQ0SuvMyGJU0dBPCZBeEYKHPi_pldWGT23zLbLry6DG&wd=&eqid=efae8130000106120000000561187322

https://www.bilibili.com/video/BV1Fo4y1d7JL?p=34&spm_id_from=pageDriver

你可能感兴趣的:(#,OpenCV,c++,opencv,计算机视觉,图像处理,python)