OpenCV_python3_13

Practical Python and OpenCV,3rd Edition 13


Gradients and Edge detection

本章主要涉及渐变和边缘检测。形式上,边缘检测体现了数学方法,以在图像中找到像素强度的亮度明显变化的点。

我们要做的第一件事是找到灰度图像的“渐变”,允许我们在x和y方向找到类似边缘的区域。

然后,我们将应用Canny边缘检测,多级降噪(模糊)过程,找到图像的梯度(利用水平和垂直方向上的Sobel核)、非最大抑制和滞后阈值。听起来似乎很难懂。这并不妨碍我们继续前进。但是,如果您对梯度和边缘检测背后的数学感兴趣,我鼓励您阅读算法。总的来说,它们并不复杂,并且可以深入了解OpenCV的幕后操作。

laplacian and sobel

import numpy as np 
import argparse
import cv2 

ap = argparse.ArgumentParser()
ap.add_argument('-i',"--image",required=True,
    help="path to the image")
args = vars(ap.parse_args())

image = cv2.imread(args["image"])
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow("orginal",image)

# Compute the Laplacian of the image
lap = cv2.Laplacian(image,cv2.CV_64F)
lap = np.uint8(np.absolute(lap))
cv2.imshow("Laplacian",lap)
cv2.waitKey(0)

当计算梯度和边缘时,我们(通常)在单个通道上计算它们——在这种情况下,我们使用灰度图像;然而,我们也可以为RGB图像的每个通道计算梯度。为了简单起见,让我们继续使用灰度图像,因为这是大多数情况下将使用的。

我们使用拉普拉斯算法通过调用cv2.Laplacian函数来计算梯度幅度图像。第一个参数是我们的灰度图像——我们想要计算梯度幅度表示的图像。第二个参数是输出图像的数据类型。

前面,我们主要使用8位无符号整数。为什么我们现在使用64位浮点数?

原因涉及图像中黑色到白色和白色到黑色的转换。

从黑色到白色的转变被认为是正斜率,而从白色到黑色的转变是负斜率。如果您还记得前面我们对图像算术的讨论,您就会知道8位无符号整数不代表负值。如果使用OpenCV,它将被剪裁为零,或者将使用NumPy执行模数运算。

这里简短的回答是,如果在计算梯度幅度图像时不使用浮点数据类型,则会丢失边缘,特别是白色到黑色的过渡。

为了确保捕获所有边,使用浮点数据类型,然后获取渐变图像的绝对值并将其转换回8位无符号整数。这绝对是一项重要的技术需要注意——否则你会丢失图像中的边缘!

让我们继续计算Sobel梯度表示

# Compute gradients along the X and Y axis, respectively
sobelX = cv2.Sobel(image,cv2.CV_64F,1,0)
sobelY = cv2.Sobel(image,cv2.CV_64F,0,1)

# The sobelX and sobelY images are now of the floating
# point data type -- we need to take care when converting
# back to an 8-bit unsigned integer that we do not miss
# any images due to clipping values outside the range
# of [0, 255]. First, we take the absolute value of the
# graident magnitude images, THEN we convert them back
# to 8-bit unsigned integers
sobelX = np.uint8(np.absolute(sobelX))
sobelY = np.uint8(np.absolute(sobelY))

# We can combine our Sobel gradient images using our
# bitwise OR
sobelCombined = cv2.bitwise_or(sobelX,sobelY)

# Show our Sobel images
cv2.imshow("Sobel X",sobelX)
cv2.imshow("Sobel Y",sobelY)
cv2.imshow("Sobel Combined",sobelCombined)
cv2.waitKey(0)

使用Sobel算子,我们可以计算沿x和y轴的梯度幅度表示,使我们能够找到水平和垂直边缘区域。

Sobel算子的第一个参数是我们想要计算梯度表示的图像。然后,就像上面的拉普拉斯算例一样,我们使用浮点数据类型。最后两个参数分别是x和y方向上导数的顺序。指定值1和0(y方向上的导数为0,平行于y轴?)以查找垂直边缘状区域,指定0和1以查找水平边缘状区域。

我们确保通过获取浮点图像的绝对值然后将其转换为8位无符号整数来找到所有边。

为了在x和y方向上组合梯度图像,我们可以应用按位OR。请记住,当任一像素大于零时,OR运算为真。因此,如果存在水平或垂直边缘,则给定像素将为True。

左上角:原始硬币图像。右上:沿x轴计算Sobel梯度大小(找到垂直边缘)。左下:沿y轴计算Sobel梯度(找到水平边缘)。右下:应用按位OR来组合两个Sobel表示。

canny edge detector

Canny边缘检测器是一个多步骤的过程。它涉及模糊图像以消除噪声,计算x和y方向上的Sobel梯度图像,抑制边缘,以及最后确定像素是否是“边缘样”的滞后阈值阶段。

新建一个canny.py文件

import numpy as np
import argparse
import cv2

# Construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required = True,
    help = "Path to the image")
args = vars(ap.parse_args())

# Load the image, convert it to grayscale, and blur it
# slightly to remove high frequency edges that we aren't
# interested in
image = cv2.imread(args["image"])
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image = cv2.GaussianBlur(image, (5, 5), 0)
cv2.imshow("Blurred", image)

# When performing Canny edge detection we need two values
# for hysteresis: threshold1 and threshold2. Any gradient
# value larger than threshold2 are considered to be an
# edge. Any value below threshold1 are considered not to
# ben an edge. Values in between threshold1 and threshold2
# are either classified as edges or non-edges based on how
# the intensities are "connected". In this case, any gradient
# values below 30 are considered non-edges whereas any value
# above 150 are considered edges.
canny = cv2.Canny(image, 30, 150)
cv2.imshow("Canny", canny)
cv2.waitKey(0)

我们要做的第一件事是导入我们的包并解析我们的参数。然后我们加载图像,将其转换为灰度,并使用高斯模糊方法对其进行模糊处理。通过在边缘检测之前应用模糊,我们将帮助消除图像中我们不感兴趣的“嘈杂”边缘。我们这里的目标是只找到硬币的轮廓。

应用Canny边缘检测器通过使用cv2.Canny函数执行。我们提供的第一个参数是模糊的灰度图像。然后,我们需要提供两个值:threshold1threshold2

任何大于threshold2的梯度值都被认为是边缘。低于threshold1的任何值都被认为不是边缘。阈值1和阈值2之间的值基于其强度如何“连接”而被分类为边缘或非边缘。在这种情况下,任何低于30的梯度值都被认为是非边缘,而高于150的任何值都被认为是边缘。

请注意边缘是如何“更清晰”。与使用拉普拉斯算子或索贝尔梯度图像时相比,我们的噪声要少得多。此外,我们的硬币轮廓清晰显示。

用到的函数

cv2.Laplacian

np.uint8

cv2.Sobel

cv2.Canny

更多的参考:

PPaO Chapter 9 – Thresholding

Zero-parameter, automatic Canny edge detection with Python and OpenCV

Histogram of Oriented Gradients and Object Detection


---------------- The End ----------------
支持一下
Fork me on GitHub ;