OpenCV中的图像处理 —— 图像梯度+Canny边缘检测

OpenCV中的图像处理 —— 图像梯度+Canny边缘检测+图像金字塔


1. 图像梯度

首先我们来看看什么是图像梯度:图像梯度可以把图像看作二维离散函数,图像梯度就是这个二维函数的求导,图像边缘一般都是通过对图像进行梯度运算来实现的

在图像梯度这一部分我们会接触查找图像梯度、边缘等,这一部分涉及了三个主要函数:cv.Sobel(),cv.Scharr(),cv.Laplacian(),相对应的,OpenCV提供的三种类型的梯度滤波器(高通滤波器),即Sobel、Scharr和Laplacian

在上一部分2D卷积即图像过滤内容中我们说了低通滤波器(LPF)与高通滤波器(HPF)的主要应用方向,LPF用于消除噪声,HPF用于找到边缘,在图像梯度这一部分我们使用三个高通滤波器来找到图像中的边缘

1.1 Sobel和Scharr算子

Sobel算子是高斯平滑与微分操作的结合体,所以其抗噪声能力很好,我们可以设定求导方向(xorder或yorder),还可以设定使用的卷积核大小ksize

当我们设定的卷积核的大小为-1时,会默认使用3x3的Scharr滤波器,它的效果比3x3的Sobel效果更好,并且处理速度相同,所以在使用3x3Sobel滤波器时应使用Scharr滤波器代替

从上面所说的概念我们可以理解为:使用3x3内核的Sobel滤波器并不等于Scharr滤波器,但Scharr滤波器是一种3x3内核的高效滤波器,若我们需要3x3内核的Sobel滤波器,那我们建议使用Scharr滤波器,即在使用Sobel滤波器时设定其内核大小为-1

了解了Sobel和Scharr高通滤波器的内核,我们再来看看cv.Sobel()和cv.Scharr()函数的参数,cv.Sobel(img,cv.CV_64F,dx,dy,ksize)函数需要传递的参数分别是原图像,cv.CV_64F是图像深度,一般写作-1就可以了,dx和dy分别表示x轴方向和y轴方向的算子,ksize就是内核大小

而Scharr高通滤波器是3x3的内核,所以cv.Scharr()的参数与cv.Sobel()函数对比少传递一个ksize参数即可

1.2 Laplacian算子

Laplace其实利用Sobel算子的运算,它通过Sobel算子运算出图像在x方向和y方向的导数,得出拉普拉斯变换结果,它就像Sobel算子的升级版

下面我们上俩例子方便大家理解

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

img = cv.imread(r'E:imagetest06.png', 0)
laplacian = cv.Laplacian(img, cv.CV_64F)
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([])
plt.subplot(2, 2, 3), plt.imshow(sobelx, cmap='gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2, 2, 4), plt.imshow(sobely, cmap='gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

注意:在上面的实例中,输出的数据类型为cv.CV_8U或np.uint8,问题就出在这里,黑色到白色的过渡被视为正斜率(具有正值),而白色到黑色的过渡被视为负斜率(具有负值),当我们将数据转换为np.uint8时,所有负斜率均设为零,意思就是我们会错过这一边缘信息

当要检测两个边缘,更好的选择是将输出数据类型保留为更高的形式,取其绝对值然后再转回cv.CV_8U

这是一个必要重要且不容忽略的问题,所以我们再通过一个例子来看看

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

img = cv.imread('E:/image/test07.png', 0)
sobelx8u = cv.Sobel(img, cv.CV_8U, 1, 0, ksize=5)

sobelx64f = cv.Sobel(img, cv.CV_64F, 1, 0, ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
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()

在这里插入图片描述

2. Canny边缘检测

Canny Edge Detection是由John F. Canny发明的一种流行的边缘检测算法,这是一个多阶段,主要分为:高斯滤波、梯度计算、非极大值抑制和双阈值检测

2.1 多阶段的Canny边缘检测算法

高斯滤波(降噪)

由于边缘检测很容易收到图像中噪声的影响,因此我们通过Canny边缘检测算法进行图像处理时第一步是使用5x5高斯滤波器消除图像中的噪声

高斯滤波的具体方法是生成一个高斯模板,使用卷积进行时进行时域滤波

梯度计算

使用Sobel核在水平和垂直方向上对平滑图像进行滤波,以在水平和垂直方向得到一阶导数

非极大值抑制

在获得梯度大小和方向后,将对图像进行全面扫描,以去除可能不构成边缘的所有不需要的像素,为此在每个像素处检查像素是都是在其梯度方向上附近的局部最大值

在这里插入图片描述

(图像来源于OpenCV4.1中文官方文档)

点A在边缘(垂直方向)上。渐变方向垂直于边缘。点B和C在梯度方向上。因此,将A点与B点和C点进行检查,看是否形成局部最大值。如果是这样,则考虑将其用于下一阶段,否则将其抑制(置为零)。 简而言之,你得到的结果是带有“细边”的二进制图像

磁滞阈值(双阈值检测)

在这个阶段会确定哪些边缘是真正的边缘,为此我们需要提供两个阈值minVal和maxVal,强度梯度大于maxVal的任何边缘必定是边缘,而小于minVal的任何边缘必定不是边缘,如果讲他们连接到边缘像素。则将他们视为边缘的一部分否则将被丢弃

边缘A在maxVal上,因此被视为“确定边缘”,尽管C低于minVal但它连接到A,因此被视为有效便,我们得到了完整的曲线

但是尽管B在minVal上并且与C处于统一区域,但是它没有连接到任何确保边缘,因此它也会丢弃

注意:我们必须选择相应的minVal和maxVal才能获取正确的结果

2.2 OpenCV中的Canny Edge检测

OpenCV将Canny边缘检测算法的四个阶段放在了一个单数cv.Canny()中,我们只需要去正确使用它就能获取我们的边缘检测需求

我们看看cv.Canny()这个函数的传参,第一个参数是图像资源,第二、三个参数分别是用于磁滞阈值(双阈值检测)阶段的两个阈值minVal和maxVal,第四个参数是picture_size,它用于查找图像渐变的Sobel内核的大小,默认为3,第五个参数是L2gradient,它指定用于查找梯度的方程式,若为True会使用更精确的公式,若为False则用默认

我们通过一个例子来看看

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

img = cv.imread('E:/image/test08.png', 0)
edges = cv.Canny(img, 100, 200)
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()

在这里插入图片描述


(注:文章内容参考OpenCV4.1中文官方文档)
如果文章对您有所帮助,记得一键三连支持一下哦

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>