【OpenCV实现图像梯度,Canny边缘检测】

news/2024/7/21 3:51:01 标签: opencv, 人工智能, 计算机视觉, python, 图像处理

文章目录

    • 概要
    • 图像梯度
    • Canny边缘检测
    • 小结

概要

OpenCV中,可以使用各种函数实现图像梯度和Canny边缘检测,这些操作对于图像处理和分析非常重要。

图像梯度通常用于寻找图像中的边缘和轮廓。在OpenCV中,可以使用cv2.Sobel()函数计算图像的梯度,该函数可以计算图像在水平和垂直方向上的梯度。梯度的方向和大小可以帮助理解图像中的边缘信息。

Canny边缘检测是一种经典的边缘检测算法,它通过多个步骤来检测图像中的边缘。首先,Canny算法使用Sobel算子计算图像的梯度。然后,它通过非极大值抑制(Non-Maximum Suppression)来细化边缘。接着,Canny算法使用双阈值(Double Thresholding)来检测强边缘和弱边缘,并通过连接强边缘来得到最终的边缘图像。Canny边缘检测在图像处理中被广泛应用,因为它能够准确地检测出图像中的边缘。

在OpenCV中,可以使用cv2.Sobel()函数计算图像的梯度,而Canny边缘检测则可以通过cv2.Canny()函数实现。通过这些函数,可以方便地在OpenCV中实现图像梯度和Canny边缘检测,进而进行各种图像分析和处理任务。

图像梯度

寻找图像梯度,边缘等等。
函数:cv.Sobel(), cv.Scharr(), cvLaplacian() 等等。

Sobel和Scharr导数:
Sobel算子是一种结合了高斯平滑和微分操作的滤波器,因此它对图像中的噪声具有较好的抵抗能力。使用Sobel算子时,可以指定所需导数的方向,通过参数yorder和xorder来确定是垂直方向还是水平方向的导数。此外,还可以通过参数ksize指定核(kernel)的大小。当ksize设为-1时,OpenCV会使用3×3的Scharr滤波器,其结果通常比3×3的Sobel滤波器更为精确。

拉普拉斯导数
它计算了由关系式给出的图像的拉普拉斯算子,其中关系式为
在这里插入图片描述
其中每一个导数都是用 Sobel 导数找到的。如果 ksize = -1,滤波器会使用下面这个内核。

在这里插入图片描述

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

# 读取图像
img = cv.imread('dave.jpg', 0)

# 计算拉普拉斯边缘
laplacian = cv.Laplacian(img, cv.CV_64F)

# 计算Sobel边缘(x和y方向)
sobelx = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
sobely = cv.Sobel(img, cv.CV_64F, 0, 1, ksize=5)

# 绘制原始图像
plt.subplot(2, 2, 1), plt.imshow(img, cmap='gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])

# 绘制拉普拉斯边缘图像
plt.subplot(2, 2, 2), plt.imshow(laplacian, cmap='gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])

# 绘制Sobel X边缘图像
plt.subplot(2, 2, 3), plt.imshow(sobelx, cmap='gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])

# 绘制Sobel Y边缘图像
plt.subplot(2, 2, 4), plt.imshow(sobely, cmap='gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])

# 显示图像
plt.show()

在这里插入图片描述
在我们的最后一个例子中,我们使用了Sobel滤波器来检测图像中的边缘。然而,当我们将输出数据类型转换为cv.CV_8U或者np.uint8时,存在一个小问题。在图像中,从黑色到白色的过渡被视为正斜率(它具有正值),而从白色到黑色的过渡被视为负斜率(它具有负值)。因此,当我们将数据类型转换为np.uint8时,所有负斜率都被截断为0,也就是说,我们会错过所有负斜率对应的边缘。

如果我们希望找到所有的边缘,更好的方法是将输出的数据类型保持在一个更高的精度范围内,例如cv.CV_16S、cv.CV_64F等,然后取其绝对值,最后再转换回cv.CV_8U类型。下面的代码演示了这个过程,特别是在处理水平Sobel滤波器时,以及结果的差异。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

# 读取灰度图像
img = cv.imread('img.png', 0)

# 使用Sobel滤波器,输出数据类型为cv.CV_8U
sobelx8u = cv.Sobel(img, cv.CV_8U, 1, 0, ksize=5)

# 使用Sobel滤波器,输出数据类型为cv.CV_64F,然后取绝对值并转换为cv.CV_8U
sobelx64f = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)

# 显示原始图像、Sobel输出(cv.CV_8U)和Sobel绝对值(cv.CV_64F)
plt.subplot(1, 3, 1), plt.imshow(img, cmap='gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 2), plt.imshow(sobelx8u, cmap='gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1, 3, 3), plt.imshow(sobel_8u, cmap='gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])

# 显示图像
plt.show()

在这里插入图片描述

Canny边缘检测

Canny边缘检测的概念
OpenCV函数:cv.Canny()

Canny边缘检测是一种广泛应用的边缘检测算法,由John F. Canny开发而成。该算法包含多个阶段,下面我们将详细介绍每个阶段的过程:

噪声减少:
由于边缘检测对图像中的噪声非常敏感,第一步是通过一个5X5的高斯滤波器对图像进行平滑处理,以去除噪声。

寻找图像中的变化(梯度)强度:
接下来,使用Sobel核在水平和垂直方向上对平滑后的图像进行滤波,得到水平方向($G_x$)和垂直方向($G_y$)的一阶导数。通过这两个导数,我们可以计算每个像素点的边缘梯度幅值和方向。梯度方向始终与边缘垂直,通常近似为水平、垂直和两个对角线方向中的一个。

在这里插入图片描述

非极大值抑制:
在得到梯度大小和方向之后,在每个像素点上进行全图扫描。对于每个像素,检查其梯度值是否在相邻像素中是最大的。如果是,保留该值,否则将其置为0。这一步骤产生了一个"薄边"的二值图像。

在这里插入图片描述
滞后阈值:
在此阶段,我们需要确定哪些边缘是真实的,哪些是噪声或者假的。为此,我们使用两个阈值,minVal(最小值)和maxVal(最大值)。所有强度大于maxVal的边缘被视为真正的边缘,低于minVal的被丢弃。介于两者之间的边缘会基于连通性来判断是否为边缘。如果它们与确定的边缘相连,则保留,否则被抛弃。这一步骤也基于边缘是长线的假设来去除小的像素噪声。
在这里插入图片描述
边缘 A 因为在 maxVal 上面,所以是肯定的边缘。尽管边缘 C 的位置在 maxVal 下面,但是因为它和边缘 A 相连接,所以我们认为它也是个有效边缘,如此得到了一条完整的曲线。但是对于边缘 B 来说,哪怕它在 minVal 上面,和边缘 C 处在同一个区域上,可是它并不和任何一条有效边缘连接,所以会被丢弃。所以,为了得到正确的结果,我们需要确定好 minVal 和 maxVal 的值。

最终,经过这些步骤,我们就能得到一条完整的边缘在图像中。

OpenCV中的Canny边缘检测

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

# 读取灰度图像
img = cv.imread('img.png', 0)

# 使用Canny边缘检测算法,阈值设置为100和200
edges = cv.Canny(img, 100, 200)

# 显示原始图像和Canny边缘检测结果
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])

plt.subplot(122), plt.imshow(edges, cmap='gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])

plt.show()

在这里插入图片描述

小结

图像梯度:

OpenCV提供了多种方法来计算图像的梯度,包括Sobel、Scharr和拉普拉斯滤波器。
Sobel算子是一种联合高斯平滑和微分操作的滤波器,可以计算图像在水平和垂直方向上的梯度。
使用Sobel算子时,你可以指定所需导数的方向(水平或垂直)以及核的大小。
拉普拉斯滤波器用于计算图像的拉普拉斯算子,通过将图像的二阶导数与Sobel导数相关联来实现。

Canny边缘检测:

Canny边缘检测是一种多阶段算法,用于检测图像中的边缘。
第一阶段是噪声减少,通常通过应用高斯滤波器来实现,以平滑图像并减少噪声。
第二阶段是计算图像的梯度,通常使用Sobel滤波器,以获得图像的水平和垂直梯度。
接下来进行非极大值抑制,以去除可能不构成边缘的像素,仅保留边缘像素。
最后一个阶段是滞后阈值,需要设置两个阈值(minVal和maxVal),以确定哪些边缘是真实的。根据这些阈值,边缘可能被分类为强边缘、弱边缘或非边缘,并进行相应的处理。

http://www.niftyadmin.cn/n/5134181.html

相关文章

【uniapp】uniapp实现input输入显示数字键盘:

文章目录 一、官网文档:二、文档:三、效果:【1】number:iPhone原生数字键盘不显示小数点【2】digit:【3】digit和inputmode: 一、官网文档: input | uni-app官网 二、文档: 三、效果…

tomcat9~10猫闪退个人经验

java版本17与8 8版本有jre,java17没有jre 所以在java8版本中将jre和jdk路径一同添加环境是不会出现闪退的,tomcat9没有闪退 但是在10就闪退了,因为java版本太低 java17没有jre,但是可以通过一种方法添加jre到java17的目录 完…

软考高项-基础知识

信息基础设施主要指基于新一代信息技术演化生成的基础设施。信息基础设施包括: 以5G、物联网、工业互联网、卫星互联网为代表的通信基础网络基础设施;以人工智能、云计算、区块链等代表的新技术基础设施;以数据中心、智能计算中心为代表的算力基础设施等。 信息基础设…

基于Qt 文本读写(QFile/QTextStream/QDataStream)实现

​ 在很多时候我们需要读写文本文件进行读写,比如写个 Mp3 音乐播放器需要读 Mp3 歌词里的文本,比如修改了一个 txt 文件后保存,就需要对这个文件进行读写操作。本章介绍简单的文本文件读写,内容精简,让大家了解文本读写的基本操作。 ## QFile 读写文本 QFile 类提供了读…

Vue 消息订阅与发布 (任意组件间通信)

也可以使用这种方法 Vue全局事件总线(任意组件间通信) 这种比较繁琐 安装命令 npm i pubsub-js 全局使用 import PubSub from pubsub-js Vue.prototype.$pubsub PubSub发送消息 Login.vue methods: {login(){ this.$pubsub.publish(getUserId, 13169…

Vue3.0组件的生命周期

每个Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段…

Java SE 学习笔记(十九)—— XML、设计模式

目录 1 XML1.1 XML 概述1.2 XML 语法规则1.3 XML 文档约束(了解)1.3.1 DTD 约束1.3.2 schema 约束 2 XML 解析2.1 XML 解析概述2.2 Dom4J 解析 XML 文件2.3 XML 解析案例 3 XML 检索4 设计模式4.1 工厂模式4.2 装饰模式 1 XML 在有些业务场景下&#xff…

在Qt中List View和List Widget的区别是什么,以及如何使用它们

2023年10月29日,周日晚上 目录 List View和List Widget的区别 如何使用QListView 如何使用QListWidget List View和List Widget的区别 在Qt中,QListView 和 QListWidget 是用于显示列表数据的两个常用控件,它们有一些区别和特点。 1. 数…