专栏首页AI异构OpenCV 边缘检测

OpenCV 边缘检测

OpenCV 边缘检测

Canny算子

Canny 边缘检测算子,其算法步骤大体如下: 1) 用高斯滤波器对输入图像做平滑处理 (大小为 5x5 的高斯核)

2) 计算图像的梯度强度和角度方向 ( xy 方向上的卷积核)

角度方向近似为四个可能值,即 0, 45, 90, 135

3) 对图像的梯度强度进行非极大抑制 可看做边缘细化:只有候选边缘点被保留,其余的点被移除

4) 利用双阈值检测和连接边缘 若候选边缘点大于上阈值,则被保留;小于下阈值,则被舍弃;处于二者之间,须视其所连接的像素点,大于上阈值则被保留,反之舍弃

OpenCV 中,Canny 函数如下:

void cv::Canny (
    InputArray    image,    // 输入图像 (8位)
    OutputArray   edges,    // 输出图像 (单通道,8位)
    double      threshold1,  // 下阈值
    double      threshold2,  // 上阈值
    int         apertureSize = 3,
    bool        L2gradient = false
)

一般 上阈值 / 下阈值 = 2 ~ 3 L2gradient 默认 flase,表示图像梯度强度的计算采用近似形式;若为 true ,则表示采用更精确的形式。

Sobel算子

假定输入图像矩阵为 I,卷积核大小为 3x3,则水平一阶导数 Gx 和垂直一阶导数 Gy 分别为:

输出的图像矩阵 G 为:

OpenCV 中,Sobel 函数如下:

void cv::Sobel   (
    InputArray  src,    // 输入图像
    OutputArray  dst,   // 输出图像
    int      ddepth,    // 输出图像深度,-1 表示等于 src.depth()
    int      dx,        // 水平方向的阶数
    int      dy,        // 垂直方向的阶数
    int     ksize = 3,    // 卷积核的大小,常取 1, 3, 5, 7 等奇数
    double  scale = 1,    // 缩放因子,应用于计算结果
    double  delta = 0,    // 增量数值,应用于计算结果
    int borderType = BORDER_DEFAULT // 边界模式
)

dxdy 表示阶数,一般取 0 或 1,但不超过 2;scale = 1,表示计算结果不缩放;delat = 0,表示计算结果无增量。

Laplace算子

索贝尔算子 (Sobel) 和拉普拉斯算子 (Laplace) 都是用来对图像进行边缘检测的,不同之处在于,前者是求一阶导,后者是求二阶导

OpenCV 中对应的函数为 Laplacian

void cv::Laplacian (
    InputArray     src,
    OutputArray    dst,
    int       ddepth,
    int       ksize = 1,
    double    scale = 1,
    double    delta = 0,
    int       borderType = BORDER_DEFAULT
)

Scharr算子

当卷积核大小为 3x3 时,使用 sobel 卷积核来计算并不是很精确,此时常用 Scharr 卷积核来代替,如下:

Sharr 函数,本质上就是令 ksize = 3 且使用 Scharr 卷积核的 Sobel 函数。

void cv::Scharr (
    InputArray  src,
    OutputArray  dst,
    int      ddepth,
    int      dx,
    int      dy,
    double  scale = 1,
    double  delta = 0,
    int     borderType = BORDER_DEFAULT
)

对于 Scharr 函数,要求 dxdy 都 >= 0 且 dx + dy == 1,假如 dxdy 都设为 1,则会抛出异常。

因此,对于 SobelScharr 函数,通常各自求其 xy 方向的导数,然后通过加权来进行边缘检测。

参考代码

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;

int main(int argc, char** argv) {
    Mat src;
    src = imread("cat.jpg");
    if (!src.data) {
        printf("could not load image...\n");
        return -1;
    }
    namedWindow("input image", CV_WINDOW_AUTOSIZE);
    imshow("input image", src);

    //自定义kernel滤波
    Mat kernelImage;
    Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
    filter2D(src, kernelImage, src.depth(), kernel);
    namedWindow("kernelImage", CV_WINDOW_AUTOSIZE);
    imshow("kernelImage", kernelImage);
    imwrite("./kernelImage.jpg",kernelImage);

    // canny算子边缘检测
    Mat canyImage;
    Canny(src, canyImage, 3, 9, 3);
    imshow("canyImage", canyImage);
    imwrite("./canyImage.jpg",canyImage);

    // Sobel算子边缘检测
    Mat sobel_x;
    Mat sobel_y;
    Mat sobelImage;
    Sobel(src, sobel_x,CV_16S,1,0,3,1,1,BORDER_DEFAULT);
    convertScaleAbs(sobel_x, sobel_x);
    Sobel(src, sobel_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
    convertScaleAbs(sobel_y, sobel_y);
    addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0, sobelImage);
    imshow("sobel_x", sobel_x);
    imshow("sobel_y", sobel_y);
    imshow("sobelImage", sobelImage);
    imwrite("./sobel_x.jpg",sobel_x);
    imwrite("./sobel_y.jpg",sobel_y);
    imwrite("./sobelImage.jpg",sobelImage);

    // laplacian算子边缘检测
    Mat laplacianImage;
    Laplacian(src, laplacianImage, CV_16S, 3, 1, 0, BORDER_DEFAULT);
    convertScaleAbs(laplacianImage, laplacianImage);
    imshow("laplacianImage", laplacianImage);
    imwrite("./laplacianImage.jpg",laplacianImage);

    // Schar算子边缘检测
    Mat scharr_x;
    Mat scharr_y;
    Mat scharrImage;
    Scharr(src, scharr_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
    convertScaleAbs(scharr_x, scharr_x);
    Scharr(src, scharr_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
    convertScaleAbs(scharr_y, scharr_y);
    addWeighted(scharr_x, 0.5, scharr_y, 0.5, 0, scharrImage);
    imshow("scharr_x", scharr_x);
    imshow("scharr_y", scharr_y);
    imshow("scharrImage", scharrImage);
    imwrite("./scharr_x.jpg",scharr_x);
    imwrite("./scharr_y.jpg",scharr_y);
    imwrite("./scharrImage.jpg",scharrImage);

    waitKey(0);
    return 0;
}

参考

[OpenCV 之 边缘检测]https://www.cnblogs.com/xinxue/p/5348743.html [OpenCV3编程入门读书笔记5-边缘检测]https://www.cnblogs.com/justkong/p/7297836.html

本文分享自微信公众号 - AI异构(gh_ed66a0ffe20a),作者:许柯

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-05-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SDAccel矩阵乘法优化(一)

    分析综合结果的方法: * 首先分析对于添加的优化指令是否综合实现,若不能实现,原因是什么? * 然后分析代码pipeline的情况。SDAccel对于嵌套的fo...

    AI异构
  • SDAccel矩阵乘法优化(二)

    首先,我们先进行访存上的优化。原始版本的矩阵乘法实现虽然简单,但是在进行计算的过程中需要频繁的与DDR进行数据交互,但是DDR与FPGA进行交互的过程中是十分耗...

    AI异构
  • SDAccel矩阵乘法优化(三)

    承接第二篇Local Memory的实现方法,接下来进一步进行矩阵乘法的优化处理。本文主要解决gmem carry dependency的问题。在这里,不采用M...

    AI异构
  • 洛谷P2118 比例简化(暴力)

    题目描述 在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果。例如,对某一观点表示支持的有1498 人,反对的有 902人,那么赞同与反对的比例可...

    attack
  • MySQL之数据库多表查询

    读取ArticleDetail表中所有文章(title)在Article表中对应的类型,作者

    ITester软件测试小栈
  • LWC 72: 787. Cheapest Flights Within K Stops

    思路: 最短路径的变种,中间限制了结点数。比如当K=1时,表示中间只能进行一次跳转,最长的路径数可以为2。定义:ds[i] 表示从起点src出发,到i,经过...

    用户1147447
  • Objective-C中runtime机制的应用

            Objective-C是一种动态语言,所谓动态语言,是在程序执行时动态的确定变量类型,执行变量类型对应的方法的。因此,在Object-C中常用字...

    珲少
  • poj-2551-ones

    Given any integer 0 <= n <= 10000 not divisible by 2 or 5, some multiple of n is...

    瑾诺学长
  • BZOJ 3680: 吊打XXX【模拟退火算法裸题学习,爬山算法学习】

    3680: 吊打XXX Time Limit: 10 Sec  Memory Limit: 128 MBSec  Special Judge Submit: 3...

    Angel_Kitty
  • 中值滤波

    中值滤波使用当前像素点和它周围的8个像素点的中值来代替当前点额像素点,这个办法对去除椒盐噪声非常有效。

    渔父歌

扫码关注云+社区

领取腾讯云代金券