【计算机视觉】轮廓检测
一、轮廓检测
在计算机视觉中,轮廓检测是另一个比较重要的任务,不单是用来检测图像或者视频帧中物体的轮廓,而且还有其他操作与轮廓检测相关。
以下代码展示了如何使用 OpenCV 进行 图像阈值处理、寻找图像轮廓 和 绘制轮廓 的完整流程:
1.1代码实现
import cv2 as cv
import numpy as np
# 1. 加载图像
img = cv.imread('example.jpg', cv.IMREAD_GRAYSCALE) # 加载为灰度图
# 2. 图像阈值处理
# 使用二值化方法对图像进行分割
_, binary = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# 3. 寻找轮廓
# cv.findContours 函数返回轮廓信息
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 4. 绘制轮廓
# 将轮廓绘制在一张彩色图像上
output = cv.cvtColor(img, cv.COLOR_GRAY2BGR) # 转为彩色图方便绘制颜色
cv.drawContours(output, contours, -1, (0, 255, 0), 2) # 绿色线条绘制所有轮廓
# 5. 显示结果
cv.imshow('Original Image', img)
cv.imshow('Binary Image', binary)
cv.imshow('Contours', output)
cv.waitKey(0)
cv.destroyAllWindows()
1.2代码详解
1. 图像阈值处理
_, binary = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
-
cv.threshold
参数:img
:输入的灰度图像。127
:阈值,像素值小于阈值的设为 0,大于阈值的设为最大值。255
:最大值(用于二值化)。cv.THRESH_BINARY
:二值化模式(也可以尝试cv.THRESH_OTSU
自动确定阈值)。
-
返回值:
_
:阈值(如果使用 OTSU 阈值化,这里会返回计算出的阈值)。binary
:二值化处理后的图像。
2. 寻找图像轮廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
-
cv.findContours
参数:binary
:输入的二值化图像。cv.RETR_TREE
:轮廓检索模式。cv.RETR_EXTERNAL
:只检索最外层轮廓。cv.RETR_LIST
:检索所有轮廓,不建立层级关系。cv.RETR_CCOMP
:检索所有轮廓并将其组织成两级结构。cv.RETR_TREE
:检索所有轮廓并建立层级结构。
cv.CHAIN_APPROX_SIMPLE
:轮廓近似方法。cv.CHAIN_APPROX_NONE
:存储轮廓上所有点。cv.CHAIN_APPROX_SIMPLE
:压缩水平、垂直和对角方向的冗余点,只保留关键点。
-
返回值:
contours
:轮廓点列表,每个轮廓是一个 Numpy 数组。hierarchy
:轮廓的层级结构信息。
3. 绘制轮廓
cv.drawContours(output, contours, -1, (0, 255, 0), 2)
cv.drawContours
参数:output
:要绘制的目标图像。contours
:轮廓列表。-1
:绘制所有轮廓。如果指定索引,则绘制特定轮廓。(0, 255, 0)
:轮廓的颜色(这里是绿色)。2
:轮廓线条的厚度。
1.3可选扩展功能
1. 寻找并标注轮廓中心点
for contour in contours:
# 计算轮廓的中心
M = cv.moments(contour)
if M["m00"] != 0: # 避免除零
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
# 绘制中心点
cv.circle(output, (cx, cy), 5, (255, 0, 0), -1) # 蓝色中心点
2. 计算轮廓面积与周长
for contour in contours:
area = cv.contourArea(contour) # 计算轮廓面积
perimeter = cv.arcLength(contour, True) # 计算轮廓周长
print(f"Area: {area}, Perimeter: {perimeter}")
3. 使用多边形近似简化轮廓
for contour in contours:
epsilon = 0.01 * cv.arcLength(contour, True) # 轮廓的近似精度
approx = cv.approxPolyDP(contour, epsilon, True) # 多边形近似
cv.drawContours(output, [approx], -1, (0, 0, 255), 2) # 红色绘制近似轮廓
1.4运行效果
- 原始图像:显示加载的灰度图。
- 二值化图像:经过阈值处理后的二值图像,轮廓清晰可见。
- 轮廓绘制:在彩色图像上绘制轮廓线。
二、查找最小闭圆轮廓案例
找到一个正方形轮廓很简单,要找到到不规则的,歪斜的以及旋转的形状,可以用OpenCV
的cv2.findContours()
函数,它能得到最好的结果。
2.1代码实现
import cv2 as cv
import numpy as np
# 1. 加载图像
img = cv.imread('example.jpg', cv.IMREAD_GRAYSCALE) # 以灰度模式加载图像
# 2. 图像二值化
_, binary = cv.threshold(img, 127, 255, cv.THRESH_BINARY) # 阈值化,生成二值图像
# 3. 寻找轮廓
contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) # 提取外轮廓
# 4. 创建用于绘制的彩色图像
output = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
# 5. 遍历所有轮廓并计算特征
for contour in contours:
# **计算矩形边界框**
x, y, w, h = cv.boundingRect(contour) # 得到普通矩形边界框
cv.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2) # 绘制绿色矩形框
# **计算最小矩形边界框**
rect = cv.minAreaRect(contour) # 最小外接矩形
box = cv.boxPoints(rect) # 获取矩形的4个顶点
box = np.int0(box) # 将顶点坐标转换为整数
cv.drawContours(output, [box], 0, (255, 0, 0), 2) # 绘制蓝色最小矩形框
# **计算最小闭圆**
(x_circle, y_circle), radius = cv.minEnclosingCircle(contour) # 计算最小外接圆
center = (int(x_circle), int(y_circle)) # 圆心坐标
radius = int(radius) # 圆半径
cv.circle(output, center, radius, (0, 0, 255), 2) # 绘制红色圆形
# 6. 显示结果
cv.imshow('Original Image', img) # 显示原始图像
cv.imshow('Contours with Features', output) # 显示绘制结果
cv.waitKey(0)
cv.destroyAllWindows()
2.2功能详解
1. 矩形边界框
x, y, w, h = cv.boundingRect(contour)
cv.boundingRect
:计算包含轮廓的普通矩形边界框。(x, y)
:矩形左上角的坐标。w, h
:矩形的宽和高。
- 绘制矩形框:
cv.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)
2. 最小矩形边界框
rect = cv.minAreaRect(contour)
box = cv.boxPoints(rect)
box = np.int0(box)
cv.minAreaRect
:计算最小外接矩形(可以倾斜)。- 返回值包含:
rect[0]
:矩形中心点(cx, cy)
。rect[1]
:矩形宽高(width, height)
。rect[2]
:旋转角度。
- 返回值包含:
cv.boxPoints
:将最小矩形的描述转换为 4 个顶点。- 绘制最小矩形:
cv.drawContours(output, [box], 0, (255, 0, 0), 2)
3. 最小闭圆
(x_circle, y_circle), radius = cv.minEnclosingCircle(contour)
cv.minEnclosingCircle
:计算轮廓的最小外接圆。(x_circle, y_circle)
:圆心坐标。radius
:圆的半径。
- 绘制圆形:
cv.circle(output, (int(x_circle), int(y_circle)), int(radius), (0, 0, 255), 2)
2.3运行效果
- 绿色矩形框:
- 传统的矩形边界框,紧紧包围住轮廓。
- 蓝色最小矩形框:
- 计算最小外接矩形,可能是倾斜的。
- 红色圆形:
- 最小外接圆,覆盖整个轮廓区域。
2.4扩展功能
1. 标注几何信息
可以在图像上标注矩形框和圆的几何参数:
cv.putText(output, f"Rect: {w}x{h}", (x, y - 10), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
cv.putText(output, f"Circle R:{radius}", (int(x_circle), int(y_circle)), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
2. 计算轮廓面积和周长
area = cv.contourArea(contour) # 计算轮廓面积
perimeter = cv.arcLength(contour, True) # 计算轮廓周长
print(f"Area: {area}, Perimeter: {perimeter}")
三、凸轮廓
凸轮廓检测 是 OpenCV 中一个常见的形状分析功能,用于检测轮廓的 凸包,即将轮廓的所有点包围起来的最小凸多边形。以下是完整的实现过程和代码
3.1代码实现
import cv2 as cv
import numpy as np
# 1. 加载图像
img = cv.imread('example.jpg', cv.IMREAD_GRAYSCALE) # 加载为灰度图像
# 2. 图像二值化处理
_, binary = cv.threshold(img, 127, 255, cv.THRESH_BINARY) # 二值化处理
# 3. 寻找轮廓
contours, _ = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 4. 转为彩色图像用于绘制
output = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
# 5. 遍历每个轮廓并计算凸包
for contour in contours:
# **计算凸包**
hull = cv.convexHull(contour) # 得到凸包的点集
# **绘制原始轮廓**
cv.drawContours(output, [contour], -1, (0, 255, 0), 2) # 绿色绘制原始轮廓
# **绘制凸包**
cv.drawContours(output, [hull], -1, (0, 0, 255), 2) # 红色绘制凸包
# 6. 显示结果
cv.imshow('Original Image', img)
cv.imshow('Contours with Convex Hull', output)
cv.waitKey(0)
cv.destroyAllWindows()
3.2代码详解
1. cv.convexHull
函数
hull = cv.convexHull(contour)
cv.convexHull
用于计算轮廓的凸包,返回凸包点集。- 参数:
contour
:输入轮廓。returnPoints
:默认值为True
,返回凸包的点集坐标。如果为False
,返回轮廓点的索引。
- 参数:
2. 绘制凸包
cv.drawContours(output, [hull], -1, (0, 0, 255), 2)
- 使用
cv.drawContours
绘制凸包。 hull
是一个封闭的多边形,可以直接用来绘制。
3.3凸性缺陷检测
如果需要检测轮廓的凸性缺陷(即轮廓与凸包之间的凹陷区域),可以使用 cv.convexityDefects
:
代码示例
for contour in contours:
# 计算凸包(返回点的索引)
hull = cv.convexHull(contour, returnPoints=False)
# 计算凸性缺陷
defects = cv.convexityDefects(contour, hull)
if defects is not None:
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
start = tuple(contour[s][0]) # 起点
end = tuple(contour[e][0]) # 终点
far = tuple(contour[f][0]) # 最远点
cv.line(output, start, end, (255, 0, 0), 2) # 绘制凸包边缘
cv.circle(output, far, 5, (0, 255, 255), -1) # 绘制凹陷点
cv.convexityDefects
:- 输入:
contour
:轮廓点集。hull
:凸包点索引。
- 返回值:
- 四个参数
(s, e, f, d)
:s
:凸包边界的起点索引。e
:凸包边界的终点索引。f
:离凸包最远的点索引。d
:缺陷深度。
- 四个参数
- 输入:
3.4运行效果
- 绿色轮廓:显示原始轮廓。
- 红色凸包:显示轮廓的最小凸多边形。
- 凸性缺陷(如果启用扩展):
- 黄色圆点表示凹陷点。
- 蓝线表示凹陷区域的边界。
四、直线和圆检测
4.1直线检测
OpenCV 中的 HoughLines 和 HoughLinesP 是常用的直线检测方法,分别用于检测 标准直线 和 概率直线。以下是这两种方法的详细介绍和代码示例
1. HoughLines(标准霍夫变换)
工作原理
霍夫变换基于直线的参数化方程:
ρ
=
x
cos
θ
+
y
sin
θ
\rho = x \cos \theta + y \sin \theta
ρ=xcosθ+ysinθ
其中:
- ( ρ (\rho (ρ):直线到坐标原点的垂直距离。
- ( θ (\theta (θ):垂线与 x 轴的夹角。
函数定义
lines = cv.HoughLines(image, rho, theta, threshold)
image
:输入二值化图像(通常为边缘图像)。rho
: ( ρ (\rho (ρ) 的分辨率(以像素为单位)。theta
: ( θ (\theta (θ) 的分辨率(以弧度为单位)。threshold
:累加器的阈值,值越高,检测到的直线越少。
代码示例
import cv2 as cv
import numpy as np
# 1. 加载图像
img = cv.imread('example.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2. 边缘检测
edges = cv.Canny(gray, 50, 150, apertureSize=3)
# 3. 使用 HoughLines 进行直线检测
lines = cv.HoughLines(edges, 1, np.pi / 180, 200)
# 4. 绘制检测到的直线
if lines is not None:
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
cv.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
# 5. 显示结果
cv.imshow('Edges', edges)
cv.imshow('Hough Lines', img)
cv.waitKey(0)
cv.destroyAllWindows()
2. HoughLinesP(概率霍夫变换)
工作原理
概率霍夫变换是标准霍夫变换的优化版本,通过随机采样边缘点,减少计算量,并直接返回直线的端点坐标。
函数定义
lines = cv.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)
image
:输入二值化图像(通常为边缘图像)。rho
: ( ρ (\rho (ρ) 的分辨率(以像素为单位)。theta
: ( θ (\theta (θ) 的分辨率(以弧度为单位)。threshold
:累加器的阈值。minLineLength
:线段的最小长度,短于此长度的线段会被忽略。maxLineGap
:线段之间的最大间隙,间隙小于此值的线段会被连接。
代码示例
import cv2 as cv
import numpy as np
# 1. 加载图像
img = cv.imread('example.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 2. 边缘检测
edges = cv.Canny(gray, 50, 150, apertureSize=3)
# 3. 使用 HoughLinesP 进行直线检测
lines = cv.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=50, maxLineGap=10)
# 4. 绘制检测到的直线
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
cv.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 5. 显示结果
cv.imshow('Edges', edges)
cv.imshow('Probabilistic Hough Lines', img)
cv.waitKey(0)
cv.destroyAllWindows()
两种方法的对比
特性 | HoughLines(标准霍夫) | HoughLinesP(概率霍夫) |
---|---|---|
检测输出 | ( ρ (\rho (ρ) 和 ( θ (\theta (θ) 的参数 | 线段的起点和终点坐标 |
绘制线段 | 绘制整个无限延长的直线 | 绘制有限长度的线段 |
计算效率 | 较低 | 较高 |
适用场景 | 对需要检测完整直线的场景较适合 | 对需要精确线段检测的场景更适合 |
总结
- HoughLines:适合检测图像中完整的直线,输出的是直线的参数化表达( ( r h o , θ (rho, \theta (rho,θ))。
- HoughLinesP:适合检测短线段或间断线段,直接返回线段的起点和终点坐标。
两种方法均可用于目标检测、形状分析和几何特征提取等任务,根据需求选择合适的算法即可。
4.2圆检测
OpenCV 的 HoughCircles
函数用于检测图像中的圆形。它基于霍夫变换的思想,能够检测不同半径的圆,并返回圆心和半径。
1. 函数定义
circles = cv.HoughCircles(image, method, dp, minDist, param1, param2, minRadius, maxRadius)
参数解释
image
:输入图像(通常是灰度图像)。method
:检测方法,使用cv.HOUGH_GRADIENT
。dp
:累加器分辨率与输入图像分辨率的反比。dp=1
表示两者相同;dp=2
表示累加器分辨率是图像分辨率的一半。minDist
:检测到的圆之间的最小距离。如果太小,可能会检测到相邻圆的重复。param1
:Canny 边缘检测的高阈值。param2
:累加器的阈值,值越大,检测到的圆越少,但更精准。minRadius
:最小半径,指定检测圆的最小可能值。maxRadius
:最大半径,指定检测圆的最大可能值。
返回值
- 返回一个包含圆心坐标和半径的二维数组:
- 每个圆的表示为
[x_center, y_center, radius]
。
- 每个圆的表示为
2. 使用示例
代码实现
import cv2 as cv
import numpy as np
# 1. 加载图像
img = cv.imread('circles.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # 转换为灰度图
# 2. 使用 HoughCircles 检测圆
circles = cv.HoughCircles(
gray,
cv.HOUGH_GRADIENT,
dp=1,
minDist=20,
param1=50,
param2=30,
minRadius=0,
maxRadius=0
)
# 3. 绘制检测到的圆
if circles is not None:
circles = np.uint16(np.around(circles)) # 将圆参数取整
for circle in circles[0, :]:
x, y, r = circle
# 绘制圆
cv.circle(img, (x, y), r, (0, 255, 0), 2)
# 绘制圆心
cv.circle(img, (x, y), 2, (0, 0, 255), 3)
# 4. 显示结果
cv.imshow('Detected Circles', img)
cv.waitKey(0)
cv.destroyAllWindows()
3. 参数调节指南
影响结果的关键参数
param1
和param2
:param1
:控制 Canny 边缘检测的敏感度。一般选50-200
之间。param2
:累加器的阈值。值越大,检测到的圆越少,但更精准。一般选择20-100
之间。
minDist
:- 圆之间的最小距离。如果过小,可能会导致重复检测;过大则可能漏检。
minRadius
和maxRadius
:- 指定圆的半径范围,设置合适值可以提高检测效率和准确率。
4. 示例运行结果
假设输入图像包含多个圆,运行结果将:
- 在每个检测到的圆上绘制一个绿色的边界。
- 在每个圆心位置绘制一个红色的小点。
5. 实用场景
- 检测硬币、球体、瞳孔等圆形物体。
- 工业检测中用于测量零件的圆形特征。
- 交通领域中用于检测圆形标志。
6. 注意事项
- 输入图像需为灰度图像:
- 如果输入为彩色图像,需先转换为灰度图:
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
- 如果输入为彩色图像,需先转换为灰度图:
- 预处理图像:
- 在检测前,使用高斯模糊减少噪声:
gray = cv.GaussianBlur(gray, (9, 9), 2)
- 在检测前,使用高斯模糊减少噪声:
- 参数调节:
- 参数需要针对具体场景多次调整,尤其是
param1
和param2
。
- 参数需要针对具体场景多次调整,尤其是
4.3椭圆检测转换圆
import cv2
import numpy as np
# 读取图像并转换为灰度
image = cv2.imread('ellipse.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化处理
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 检测轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 假设我们只使用最大的轮廓
largest_contour = max(contours, key=cv2.contourArea)
# 拟合椭圆
ellipse = cv2.fitEllipse(largest_contour)
(center, axes, angle) = ellipse # 中心、长短轴、旋转角度
major_axis = max(axes) / 2
minor_axis = min(axes) / 2
# 计算椭圆外接圆半径
radius = major_axis
# 定义外接圆的四个点(在圆上)
circle_corners = np.array([
[center[0] - radius, center[1] - radius], # 左上
[center[0] + radius, center[1] - radius], # 右上
[center[0] + radius, center[1] + radius], # 右下
[center[0] - radius, center[1] + radius], # 左下
], dtype="float32")
# 定义椭圆四个边缘点
ellipse_corners = cv2.ellipse2Poly((int(center[0]), int(center[1])),
(int(major_axis), int(minor_axis)),
int(angle), 0, 360, 90)
ellipse_corners = np.float32(ellipse_corners[[0, 1, 2, 3]]) # 取4个关键点
# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(ellipse_corners, circle_corners)
# 透视变换
output_size = (int(radius * 2), int(radius * 2)) # 输出图像大小
transformed = cv2.warpPerspective(image, M, output_size)
# 定位圆心(在透视变换后为图像中心)
circle_center = (output_size[0] // 2, output_size[1] // 2)
# 将椭圆上的一个点转换到圆形坐标系
elliptical_point = np.array([[center[0] + major_axis * 0.5, center[1]]], dtype="float32") # 椭圆右侧中点
elliptical_point = np.array([elliptical_point]) # 需要额外添加一个维度
transformed_point = cv2.perspectiveTransform(elliptical_point, M)
# 绘制椭圆内的原始点和转换后的点
cv2.circle(image, (int(elliptical_point[0][0][0]), int(elliptical_point[0][0][1])), 5, (255, 0, 0), -1) # 原始点
cv2.circle(transformed, (int(transformed_point[0][0][0]), int(transformed_point[0][0][1])), 5, (0, 255, 0), -1) # 转换后的点
# 显示结果
cv2.imshow('Original Ellipse', image)
cv2.imshow('Transformed Circle', transformed)
cv2.waitKey(0)
cv2.destroyAllWindows()
这段代码的目标是:
- 检测图像中的椭圆。
- 利用椭圆的外接圆定义一个校正区域。
- 对椭圆进行透视变换,将其校正为圆形,并定位圆心位置。
1. 图像读取和灰度转换
image = cv2.imread('ellipse.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imread
:读取输入图像。cv2.cvtColor
:将彩色图像转换为灰度图像,便于后续处理。
2. 二值化处理
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv2.threshold
:对灰度图像进行二值化操作,将像素值分为两种:低于 127 的像素置为 0(黑色),高于 127 的像素置为 255(白色)。- 输出:二值化后的图像
binary
,用于轮廓检测。
3. 检测轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
largest_contour = max(contours, key=cv2.contourArea)
cv2.findContours
:在二值化图像中检测轮廓。cv2.RETR_TREE
:检测所有轮廓并建立层级关系。cv2.CHAIN_APPROX_SIMPLE
:压缩轮廓以减少存储空间。
max
:选择面积最大的轮廓largest_contour
,假设这是目标椭圆。
4. 椭圆拟合
ellipse = cv2.fitEllipse(largest_contour)
(center, axes, angle) = ellipse
major_axis = max(axes) / 2
minor_axis = min(axes) / 2
cv2.fitEllipse
:拟合椭圆,返回(中心点, 长短轴, 旋转角度)
。center
:椭圆中心坐标。axes
:椭圆的长轴和短轴。angle
:椭圆的旋转角度。
major_axis
和minor_axis
:分别是长轴和短轴的半径。
5. 计算椭圆的外接圆
radius = major_axis
circle_corners = np.array([
[center[0] - radius, center[1] - radius], # 左上
[center[0] + radius, center[1] - radius], # 右上
[center[0] + radius, center[1] + radius], # 右下
[center[0] - radius, center[1] + radius], # 左下
], dtype="float32")
- 外接圆的半径
radius
等于椭圆长轴的一半。 - 定义外接圆的四个角点(以椭圆中心为圆心,向四个方向延伸
radius
距离)。
6. 定义椭圆的四个边缘点
ellipse_corners = cv2.ellipse2Poly((int(center[0]), int(center[1])),
(int(major_axis), int(minor_axis)),
int(angle), 0, 360, 90)
ellipse_corners = np.float32(ellipse_corners[[0, 1, 2, 3]]) # 取4个关键点
cv2.ellipse2Poly
:生成椭圆上的离散点。- 输入参数包括椭圆的中心、长短轴、旋转角度等。
- 每隔
90°
取一个点,表示椭圆的四个边缘点。
- 通过索引
[0, 1, 2, 3]
获取四个关键点。
7. 计算透视变换矩阵
M = cv2.getPerspectiveTransform(ellipse_corners, circle_corners)
cv2.getPerspectiveTransform
:计算从椭圆的四个边缘点到外接圆角点的透视变换矩阵M
。
8. 透视变换到圆形
output_size = (int(radius * 2), int(radius * 2)) # 输出图像大小
transformed = cv2.warpPerspective(image, M, output_size)
cv2.warpPerspective
:使用透视变换矩阵M
对原图进行变换,生成一个圆形区域。- 输出图像的大小为外接圆的直径。
9. 定位圆心
circle_center = (output_size[0] // 2, output_size[1] // 2)
- 透视变换后的圆心对应输出图像的正中心。
10. 可视化结果
cv2.circle(transformed, circle_center, 5, (0, 0, 255), -1) # 标记圆心
cv2.imshow('Transformed Circle', transformed)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.circle
:在变换后的图像上绘制圆心。cv2.imshow
:显示最终结果。
4.4多边形检测
在 OpenCV 中,可以使用轮廓检测方法对图像中的任意多边形进行检测。
实现步骤
-
读取图像并预处理:
- 转换为灰度图像。
- 对图像进行二值化或边缘检测(如 Canny 算法)。
-
检测轮廓:
- 使用
cv2.findContours
方法提取图像中的轮廓。
- 使用
-
轮廓逼近:
- 使用
cv2.approxPolyDP
对轮廓点进行逼近,从而提取多边形。
- 使用
-
可视化多边形:
- 绘制检测到的多边形。
代码示例
import cv2
import numpy as np
# 1. 读取图像并转换为灰度
image = cv2.imread('polygon.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 2. 图像预处理(可以选择 Canny 或二值化)
# 使用 Canny 边缘检测
edges = cv2.Canny(gray, 50, 150)
# 或者直接使用二值化
# _, edges = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 3. 检测轮廓
contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 4. 遍历每个轮廓,进行多边形拟合
for contour in contours:
# 计算轮廓的周长
perimeter = cv2.arcLength(contour, True)
# 进行多边形逼近
epsilon = 0.02 * perimeter # 调整逼近精度,越小越精确
approx = cv2.approxPolyDP(contour, epsilon, True)
# 绘制多边形轮廓
cv2.drawContours(image, [approx], 0, (0, 255, 0), 2)
# 显示多边形的顶点数
text = f"{len(approx)}-sides"
x, y = approx[0][0] # 获取多边形的第一个顶点坐标
cv2.putText(image, text, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
# 5. 显示结果
cv2.imshow('Detected Polygons', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
关键方法解析
-
cv2.findContours
:- 用于提取二值图像的轮廓。
- 参数:
cv2.RETR_TREE
:提取所有轮廓并构建层级关系。cv2.CHAIN_APPROX_SIMPLE
:存储轮廓关键点,压缩冗余点。
-
cv2.approxPolyDP
:- 用于将轮廓逼近为多边形。
- 参数:
epsilon
:逼近精度,值为轮廓周长的一个比例。True
:表示轮廓是否封闭。
-
cv2.arcLength
:- 用于计算轮廓的周长,帮助确定
epsilon
。
- 用于计算轮廓的周长,帮助确定
调整参数
epsilon
值:- 越小,多边形越接近原始轮廓(更精确)。
- 越大,顶点数越少(近似程度更高)。
- 根据图像复杂程度适当调整
cv2.Canny
的阈值或二值化参数。
检测效果
- 对于规则图形(如矩形、三角形等),可以准确检测顶点。
- 对于不规则形状,输出的多边形是逼近的结果。