使用 OpenCV 绘制轮廓

42 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

轮廓简介

轮廓可以简单地理解为连接图像中对象所有边界连续点的曲线,轮廓通常具有相同的颜色或强度。轮廓是形状分析、物体检测等应用中的重要工具。为了得到更好的轮廓精度,可以使用二值图像检测对象轮廓。因此,在检测图像轮廓之前,通常需要对图像应用阈值处理或 Canny 边缘检测算法。

使用 OpenCV 绘制轮廓

OpenCV 中,可以使用 cv2.threshold() 函数使用固定阈值创建二值图像:

cv2.threshold(img, thresh, maxval, type)

使用 Canny 算法检测图像边缘:

Canny(img, threshold1, threshold2, apertureSize = 3, L2gradient = false)

Canny() 函数将输入灰度图像和两个滞后阈值(最大阈值和最小阈值)作为输入,其核心思想在于,当像素值>最大阈值时,其被视为强边缘像素,而在最小阈值和最大阈值之间的像素值被认为是弱边缘像素。

强边将包含在边缘图中,而弱边仅当它们连接到强边时才会包含在边缘图中,通常,上阈值的大小为下阈值的 1.5–2 倍。

使用 findContours() 函数可以计算灰度图像的轮廓。首先,需要使用以上两个函数将图像转换为二进制图像,然后计算图像轮廓。此外,需要注意的是,使用 OpenCV 函数查找轮廓需要从黑色背景中查找白色对象,要检测的对象是白色的,背景是黑色的。findContours() 函数接受三个参数,用于查找二值图像中的轮廓:

cv2.findContours(img, mode, method)

首先,导入所需的库。使用 OpenCVimread() 函数从磁盘读取输入图像,OpenCVBGR 格式存储 RGB 图像,如果想要使用 matplotlib.pyplot 正确显示,我们需要使用 cv2.cvtClor() 函数将其转换为 RGB 格式,然后绘制图像:

import cv2
import numpy as np
import matplotlib.pyplot as plt

image = cv2.imread("1.png")
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 将 BGR 模式转换为 RGB

将图像转换为灰度图像,并使用具有合适滞后阈值的 Canny 边缘检测器来查找图像中的边缘,然后从二值边缘图像中查找轮廓:

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.Canny(gray, 125, 250)
contours_edged, _ = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("Number of Contours found with Canny edges = " + str(len(contours_edged)))

或者使用阈值函数将灰度图像转换为二值图像,然后根据阈值图像中查找轮廓:

ret, thresh = cv2.threshold(gray, 127, 255, 0)
contours_thresh, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print("Number of Contours found with threshold = " + str(len(contours_thresh)))

使用 matplotlib.pyplot 模块的 imshow() 函数显示原始图像、阈值图像和轮廓图像:

plt.figure(figsize=(20,15))
plt.subplot(221), plt.imshow(image), plt.title('Original Image', size=10), plt.axis('off')
plt.subplot(222), plt.imshow(thresh, cmap='gray'), plt.title('Threshold Image', size=10), plt.axis('off')
plt.subplot(223), plt.imshow(edged, cmap='gray'), plt.title('Canny Edges Image', size=10), plt.axis('off')
plt.subplot(224), plt.imshow(cv2.drawContours(np.copy(image), contours_thresh, -1, (0,255,0), 3))
plt.title('Contour Lines with Threshold Image', size=10), plt.axis('off')

Figure_15.png

最后,从边缘二值图像中绘制前 n=500 个轮廓:

n = 200
plt.figure(figsize=(7,7))
colors = plt.cm.coolwarm(np.linspace(0, 1, n))
for i in range(n):
    image = cv2.drawContours(image, contours_edged, i, 255*colors[i], 3)
plt.imshow(image)
plt.title('First ' + str(n) + ' Contour lines with Canny Edges', size=10), plt.axis('off')
plt.tight_layout()
plt.show()

Figure_16.png