Sobel 导数

目标

本文档尝试解答如下问题:

  • 如何使用OpenCV函数 Sobel 对图像求导。
  • 如何使用OpenCV函数 Scharr 更准确地计算  核的导数。

原理

Note

以下内容来自于Bradski和Kaehler的大作: Learning OpenCV .

  1. 上面两节我们已经学习了卷积操作。一个最重要的卷积运算就是导数的计算(或者近似计算).

  2. 为什么对图像进行求导是重要的呢? 假设我们需要检测图像中的 边缘 ,如下图:

    你可以看到在 边缘 ,相素值显著的 改变 了。表示这一 改变 的一个方法是使用 导数 。 梯度值的大变预示着图像中内容的显著变化。

  3. 用更加形象的图像来解释,假设我们有一张一维图形。下图中灰度值的”跃升”表示边缘的存在:

  4. 使用一阶微分求导我们可以更加清晰的看到边缘”跃升”的存在(这里显示为高峰值)

  5. 从上例中我们可以推论检测边缘可以通过定位梯度值大于邻域的相素的方法找到(或者推广到大于一个阀值).

  6. 更加详细的解释,请参考Bradski 和 Kaehler的 Learning OpenCV 。

Sobel算子

  1. Sobel 算子是一个离散微分算子 (discrete differentiation operator)。 它用来计算图像灰度函数的近似梯度。
  2. Sobel 算子结合了高斯平滑和微分求导。

计算

假设被作用图像为 :

  1. 在两个方向求导:

    1. 水平变化: 将  与一个奇数大小的内核  进行卷积。比如,当内核大小为3时, 的计算结果为:

    2. 垂直变化: 将:math:I 与一个奇数大小的内核  进行卷积。比如,当内核大小为3时,  的计算结果为:

  2. 在图像的每一点,结合以上两个结果求出近似 梯度:

    有时也用下面更简单公式代替:

Note

当内核大小为  时, 以上Sobel内核可能产生比较明显的误差(毕竟,Sobel算子只是求取了导数的近似值)。 为解决这一问题,OpenCV提供了 Scharr 函数,但该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确,其内核为:

关于( Scharr )的更多信息请参考OpenCV文档。在下面的示例代码中,你会发现在 Sobel 函数调用的上面有被注释掉的 Scharr 函数调用。 反注释Scharr调用 (当然也要相应的注释掉Sobel调用),看看该函数是如何工作的。

源码

  1. 本程序做什么?

    • 使用 Sobel算子 产生的输出图像上,检测到的亮起的 边缘 相素散布在更暗的背景中。
  2. 下面是本教程的源码,你也可以从 here 下载
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h> using namespace cv; /** @function main */
int main( int argc, char** argv )
{ Mat src, src_gray;
Mat grad;
char* window_name = "Sobel Demo - Simple Edge Detector";
int scale = 1;
int delta = 0;
int ddepth = CV_16S; int c; /// 装载图像
src = imread( argv[1] ); if( !src.data )
{ return -1; } GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT ); /// 转换为灰度图
cvtColor( src, src_gray, CV_RGB2GRAY ); /// 创建显示窗口
namedWindow( window_name, CV_WINDOW_AUTOSIZE ); /// 创建 grad_x 和 grad_y 矩阵
Mat grad_x, grad_y;
Mat abs_grad_x, abs_grad_y; /// 求 X方向梯度
//Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_x, abs_grad_x ); /// 求Y方向梯度
//Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
convertScaleAbs( grad_y, abs_grad_y ); /// 合并梯度(近似)
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad ); imshow( window_name, grad ); waitKey(0); return 0;
}

解释

  1. 首先申明变量:

    Mat src, src_gray;
    Mat grad;
    char* window_name = "Sobel Demo - Simple Edge Detector";
    int scale = 1;
    int delta = 0;
    int ddepth = CV_16S;
  2. 装载原图像 src:

    src = imread( argv[1] );
    
    if( !src.data )
    { return -1; }
  3. 第一步对原图像使用 GaussianBlur 降噪 ( 内核大小 = 3 )

    GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );
    
  4. 将降噪后的图像转换为灰度图:

    cvtColor( src, src_gray, CV_RGB2GRAY );
    
  5. 第二步,在 x 和 y 方向分别”求导“。 为此,我们使用函数 Sobel :

    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y; /// 求 X方向梯度
    Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
    /// 求 Y方向梯度
    Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );

    该函数接受了以下参数:

    • src_gray: 在本例中为输入图像,元素类型 CV_8U
    • grad_x/grad_y: 输出图像.
    • ddepth: 输出图像的深度,设定为 CV_16S 避免外溢。
    • x_orderx 方向求导的阶数。
    • y_ordery 方向求导的阶数。
    • scaledelta 和 BORDER_DEFAULT: 使用默认值

    注意为了在 x 方向求导我们使用:  , . 采用同样方法在 y 方向求导。

  6. 将中间结果转换到 CV_8U:

    convertScaleAbs( grad_x, abs_grad_x );
    convertScaleAbs( grad_y, abs_grad_y );
  7. 将两个方向的梯度相加来求取近似 梯度 (注意这里没有准确的计算,但是对我们来讲已经足够了)。

    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
    
  8. 最后,显示结果:

    imshow( window_name, grad );
    

结果

  1. 这里是将Sobel算子作用于 lena.jpg 的结果:

Sobel导数的更多相关文章

  1. 2.7 Sobel导数

    OpenCV函数 Sobel(src_gray,grad_x/grad_y,ddepth,x_order,y_order,scale,delta,BORDER_DEFAULT ) Scharr( ) ...

  2. OpenCV Sobel 导数

    #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #i ...

  3. opencv-学习笔记(6)图像梯度Sobel以及canny边缘检测

    opencv-学习笔记(6)图像梯度Sobel以及canny边缘检测 这章讲了 sobel算子 scharr算子 Laplacion拉普拉斯算子 图像深度问题 Canny检测 图像梯度 sobel算子 ...

  4. 2.opencv图像处理常用操作

    图像的平滑处理 平滑,也称 模糊, 平滑处理时需要用到一个滤波器 .滤波器想象成一个包含加权系数的窗口,这个加权系数也叫做核或者模版. // 图像平滑处理分而学之.cpp : 定义控制台应用程序的入口 ...

  5. 【OpenCV新手教程第14】OpenCVHough变换:霍夫变换线,霍夫变换圆汇编

    本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/26977557 作者:毛星云(浅墨) ...

  6. SciPy模块应用

    1.图像模糊  图像的高斯模糊是非常经典的图像卷积例子.本质上,图像模糊就是将(灰度)图像I 和一个高斯核进行卷积操作:,其中是标准差为σ的二维高斯核.高斯模糊通常是其他图像处理操作的一部分,比如图像 ...

  7. [OpenCV-Python] OpenCV 中的图像处理 部分 IV (二)

    部分 IVOpenCV 中的图像处理 OpenCV-Python 中文教程(搬运)目录 16 图像平滑 目标 • 学习使用不同的低通滤波器对图像进行模糊 • 使用自定义的滤波器对图像进行卷积(2D 卷 ...

  8. [OpenCV-Python] OpenCV 中机器学习 部分 VIII

    部分 VIII机器学习 OpenCV-Python 中文教程(搬运)目录 46 K 近邻(k-Nearest Neighbour ) 46.1 理解 K 近邻目标 • 本节我们要理解 k 近邻(kNN ...

  9. OpenCV常用库函数[典]

    一.core 模块 1.Mat - 基本图像容器 Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是 ...

随机推荐

  1. ACdream 1216 (ASC训练1) Beautiful People(DP)

    题目地址:http://acdream.info/problem? pid=1216 这题一開始用的是线段树.后来发现查询的时候还须要DP处理.挺麻烦..也就不了了之了..后来想到,这题事实上就是一个 ...

  2. 怎样取消不能改动(仅仅读打开)的word文件的password

    作者:iamlaosong 朋友给我一个文档,是加了防改动password的,希望我能帮其取消.由于须要原文档的格式,取消方法例如以下(office2007环境): 1.打开文件.文件打开时,提演示样 ...

  3. C#趣味程序---理財高手

    问题:如果银行存款分五种 利率:0.63%  一年   月 利率:0.66%  二年   月 利率:0.69%  三年   月 利率:0.75%  五年   月 利率:0.84%  八年   月 如今 ...

  4. hive beeline 的server启动与连接

    启动hiveServer2 启动beeline之后 连接 !connect jdbc:hive2://localhost:10000/default 启动的时候连接 /beeline -u jdbc: ...

  5. PHP 使用 GeoLiteCity 库解析 IP 为地理位置

    关于把 IP 地址转换为地理位置可以使用网络上很多的 API,好处就是不用在本地存储一个 IP 数据库,而且一般网络上的 IP 库会自动更新,不利的地方就是太依赖于网络,性能表现也可能会弱些.比如像下 ...

  6. 【WPF学习笔记】之依赖属性

    概述: Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能.这些服务通常统称为 WPF 属性系统.由 ...

  7. str_replace 替换 小技巧

    // $id:1 $id:1,2,3,4,5 public function delete($id) { // 把1替换掉不允许删除ID为1的角色             在前面分别加了 , 号   ...

  8. CSMA/CD解释与理解

    1. CSMA/CD含义 CSMA/CD即载波监听多点接入/碰撞检测,此协议是使用在总线型网络中的,不同计算机是通过多点接入的方式连接在一起.协议的重点在于监听和碰撞检测. 2. 为什么要监听和碰撞检 ...

  9. cnn 实例

    http://www.geekcome.com/content-10-3761-1.html http://www.geekcome.com/content-10-3761-1.html http:/ ...

  10. C语言基础知识【运算符】

    C 运算符1.运算符是一种告诉编译器执行特定的数学或逻辑操作的符号.C 语言内置了丰富的运算符,并提供了以下类型的运算符:算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符2.杂项运算符 ↦ ...