1 轮廓描边
cv2.findContours()
函数是OpenCV中用于寻找轮廓的函数之一。它可以用于在二值图像中查找并检测出所有的物体轮廓,以及计算出这些轮廓的各种属性,例如面积、周长、质心等。
cv2.findContours()
函数的语法如下:
contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
其中,参数含义如下:
image
:输入二值图像。mode
:轮廓检索模式。有三种可选模式:cv2.RETR_EXTERNAL
,表示只检测外部轮廓;cv2.RETR_LIST
,表示检测所有轮廓,并将它们存储在列表中;cv2.RETR_TREE
,表示检测所有轮廓,并构建轮廓的完整层次结构。method
:轮廓近似方法。有四种可选方法:cv2.CHAIN_APPROX_NONE
,表示存储所有的轮廓点;cv2.CHAIN_APPROX_SIMPLE
,表示仅存储轮廓的终点坐标;cv2.CHAIN_APPROX_TC89_L1
和cv2.CHAIN_APPROX_TC89_KCOS
,表示使用Teague的链逼近算法来压缩轮廓点。contours
:可选参数,用于存储检测到的轮廓。如果提供了该参数,则函数会将轮廓存储在该参数中。hierarchy
:可选参数,用于存储轮廓的层次结构信息。如果提供了该参数,则函数会将层次结构信息存储在该参数中。offset
:可选参数,指定检测到的轮廓点的偏移量。默认为(0, 0)
。
cv2.findContours()
函数会返回两个值:contours
和 hierarchy
。其中,contours
是一个包含所有检测到的轮廓的Python列表,每个轮廓都是由一组点构成的Numpy数组。而 hierarchy
是一个包含轮廓的层次结构信息的Numpy数组。
因为输入图像必须是二值图像,所以需要先将彩色图像转变为灰度图像,再转换为二值图像,才能输入进参数值中。
下面尝试对前面文章的主图进行描边:
import cv2
# 读入图像
img = cv2.imread('test.png')
# 将图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 对灰度图像进行二值化处理
ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 在图像上绘制轮廓
cv2.drawContours(img, contours, -1, (0, 0, 255), 1)
# 显示结果
cv2.imshow('Original', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
这段代码首先使用 cv2.imread()
函数读入图像,然后使用 cv2.cvtColor()
函数将该图像转换为灰度图像。接着,我们对灰度图像进行了二值化处理,并使用 cv2.findContours()
函数查找图像中的轮廓。最后使用 cv2.drawContours()
函数将所有轮廓绘制在原始图像上,并使用 cv2.imshow()
函数显示处理后的图像。
2 轮廓拟合
轮廓拟合可以将凹凸不平的轮廓以平滑曲线进行连接。常用的有矩形拟合cv2.boundingRect
和圆形拟合cv2.minEnclosingCircle
。二者都是在cv2.findContours()
的基础上进行拟合。
2.1 最小外接矩形
cv2.boundingRect()
函数可以用于计算轮廓的最小外接矩形,即将轮廓拟合到一个矩形框中,并返回该矩形框的左上角坐标以及宽度和高度。该函数的语法如下:
x, y, w, h = cv2.boundingRect(contour)
其中,contour
是一个Numpy数组,表示待计算的轮廓。函数返回的 x
和 y
分别表示最小外接矩形的左上角坐标,而 w
和 h
则分别表示矩形的宽度和高度
2.2 最小外接圆
cv2.minEnclosingCircle()
函数可以用于计算轮廓的最小外接圆,即将轮廓拟合到一个圆形中,并返回该圆的圆心坐标和半径。该函数的语法如下:
(x, y), radius = cv2.minEnclosingCircle(contour)
其中,contour
是一个Numpy数组,表示待计算的轮廓。函数返回的 (x, y)
是最小外接圆的圆心坐标,而 radius
则是圆的半径。
2.3 示例代码
import cv2
# 读入图像
img = cv2.imread('test.png')
# 将图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 对灰度图像进行二值化处理
ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制最小外接矩形和最小外接圆
for contour in contours:
# 计算最小外接矩形
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
# 计算最小外接圆
(x, y), radius = cv2.minEnclosingCircle(contour)
center = (int(x), int(y))
radius = int(radius)
cv2.circle(img, center, radius, (0, 255, 0), 2)
# 显示结果
cv2.imshow('Original', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最终,基本上每个文章都被画上了矩形框与圆形框。
3 凸包
OpenCV中的凸包是一个用于计算给定点集的凸包形状的函数。凸包是一个包围给定点集的最小凸多边形或凸多面体。凸多边形或凸多面体的特点是其边缘不会凹陷(可以理解为不会有大于180°的角),而是向外弯曲,使得所有点都位于其内部或边缘上。
在OpenCV中,可以使用cv2.convexHull()函数来计算给定点集的凸包。该函数接受一个点集作为输入,并返回凸包形状的点集。该函数使用的算法称为Graham-Scan算法,该算法在O(n log n)时间内计算给定点集的凸包。
示例代码如下:
import cv2
# 读入图像
img = cv2.imread('test.png')
# 将图像转换为灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 对灰度图像进行二值化处理
ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY)
# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制凸包
for contour in contours:
hull = cv2.convexHull(contour)
cv2.polylines(img, [hull], True, (0, 0, 255), 2)
# 显示结果
cv2.imshow('Original', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
4 Canny边缘检测
Canny边缘检测是一种广泛使用的方法,用于检测图像中的边缘。在OpenCV中,可以使用cv2.Canny()函数来执行Canny边缘检测。
cv2.Canny()函数使用以下步骤来检测图像中的边缘:
- 降噪:使用高斯滤波器对输入图像进行模糊处理,以减少图像中的噪声。
- 计算梯度:对模糊图像使用Sobel算子来计算图像中每个像素的梯度。这可以帮助识别像素值变化最剧烈的位置,即可能的边缘。
- 非极大值抑制:在图像中沿着梯度方向找到局部最大值,将它们保留为可能的边缘点,而抑制其他非最大值的像素点。
- 双阈值检测:根据两个阈值(高阈值和低阈值)将可能的边缘点分类为强边缘和弱边缘。如果像素值大于高阈值,则被标记为强边缘;如果像素值在低阈值和高阈值之间,则被标记为弱边缘。如果像素值低于低阈值,则被抑制。
- 边缘连接:在图像中连接强边缘,并删除与弱边缘不相连的弱边缘。
通过调整高阈值和低阈值,可以影响Canny边缘检测的结果。较高的阈值将产生更少的强边缘,而较低的阈值将产生更多的弱边缘。通常建议将高阈值设置为低阈值的3到2倍之间。
下面的代码展示了阈值对边缘检测的影响:
import cv2
img = cv2.imread("test.png") # 读取原图
r1 = cv2.Canny(img, 25, 50) # 使用不同的阈值进行边缘检测
r2 = cv2.Canny(img, 100, 200)
r3 = cv2.Canny(img, 200, 400)
cv2.imshow("img", img)
cv2.imshow("r1", r1) # 显示边缘检测结果
cv2.imshow("r2", r2)
cv2.imshow("r3", r3)
cv2.waitKey()
cv2.destroyAllWindows()
5 霍夫变换
霍夫变换(Hough Transform)是一种常见的图像处理技术,用于检测图像中的各种形状。在OpenCV中,可以使用cv2.HoughLines()和cv2.HoughCircles()函数来执行霍夫变换。
5.1 检测直线
cv2.HoughLines()
函数用于检测二维平面上的直线。该函数接受二值化图像和一些可选参数作为输入,并返回检测到的直线的端点坐标。在进行霍夫变换之前,需要使用cv2.Canny()
函数对输入图像进行边缘检测。函数将返回一个二维数组,其中每一行表示检测到的一条直线的两个端点坐标。
cv2.HoughLines()
函数语法如下:
lines = cv2.HoughLines(image, rho, theta, threshold, lines=None, srn=None, stn=None, min_theta=None, max_theta=None)
其中,参数含义如下:
image
:输入的二值化图像,即通过cv2.Canny()等函数得到的边缘图像。rho
:表示距离精度,以像素为单位。通常取值为1。theta
:表示角度精度,以弧度为单位。通常取值为np.pi/180,表示以1度为单位。threshold
:表示在霍夫空间中的最小投票数,用于确定一条直线是否存在。通常取值范围为0到图像宽高之和的一半。lines
:可选输出参数,用于存储检测到的直线,以numpy.ndarray格式返回。srn
:可选参数,表示霍夫梯度中径向和角度分辨率之间的参数,通常取默认值0。stn
:可选参数,表示霍夫梯度中两个角度之间的分辨率,通常取默认值0。min_theta
和max_theta
:可选参数,表示需要检测的直线的角度范围,以弧度为单位。
该函数将返回一个包含检测到的直线的端点坐标的numpy.ndarray数组。数组中每一行表示检测到的一条直线,包括两个端点的rho和theta值。
霍夫变换是一种计算密集型算法,处理大型图像时可能需要一定的计算时间。此外,霍夫变换对输入图像的质量要求较高,因此必须在边缘检测和二值化等预处理步骤上进行仔细的参数调整和图像优化,以获得最佳结果。
检测图像中存在直线的代码:
import cv2
import numpy as np
img = cv2.imread("test.png") # 读取原图
o = img.copy() # 复制原图
o = cv2.medianBlur(o, 5) # 使用中值滤波进行降噪
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY) # 从彩色图像变成单通道灰度图像
binary = cv2.Canny(o, 50, 150) # 绘制边缘图像
# 检测直线,精度为1,全角度,阈值为100,线段最短100,最小间隔为30
lines = cv2.HoughLinesP(binary, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=30)
for line in lines:
x1, y1, x2, y2 = line[0] # 读取直线两个端点的坐标
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2) # 在原始图像上绘制直线
cv2.imshow("canny", binary)
cv2.imshow("img", img)
cv2.waitKey()
cv2.destroyAllWindows()
5.2 检测圆形
cv2.HoughCircles()
函数用于检测圆形。该函数接受二值化图像、检测算法的参数以及圆的最小和最大半径作为输入,并返回检测到的圆的圆心坐标和半径。函数将返回一个三维数组,其中每个元素表示检测到的一个圆形,包括圆心坐标和半径。
cv2.HoughCircles()
函数语法如下:
circles = cv2.HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None)
其中:
image
:输入的二值化图像,即通过cv2.Canny()等函数得到的边缘图像。method
:表示霍夫变换检测圆形的方法。目前支持两种方法:cv2.HOUGH_GRADIENT和cv2.HOUGH_GRADIENT_ALT。dp
:表示霍夫梯度中心之间的累加器分辨率的反比例系数。通常取值为1,表示与图像像素一样的分辨率。minDist
:表示圆形之间的最小距离,以像素为单位。通常取值为圆形直径的两倍左右。circles
:可选输出参数,用于存储检测到的圆形,以numpy.ndarray格式返回。param1
:表示边缘检测的高阈值,用于辅助检测图像中的圆形。通常取值为100。param2
:表示霍夫变换中的累加器阈值,用于确定检测到的圆形是否存在。通常取值为30到50之间。minRadius
和maxRadius
:可选参数,表示需要检测的圆形的半径范围,以像素为单位。
该函数将返回一个包含检测到的圆形的圆心坐标和半径的numpy.ndarray数组。数组中每一行表示检测到的一个圆形,包括三个参数:圆心坐标x、y和半径r。
代码使用与检测直线非常相似,这里就不写示例代码了。