【计算机视觉】轮廓检测
一、轮廓检测
在计算机视觉中,轮廓检测是另一个比较重要的任务,不单是用来检测图像或者视频帧中物体的轮廓,而且还有其他操作与轮廓检测相关。
以下代码展示了如何使用 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 * rhoy0 = b * rhox1 = 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_axiscircle_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
的阈值或二值化参数。
检测效果
- 对于规则图形(如矩形、三角形等),可以准确检测顶点。
- 对于不规则形状,输出的多边形是逼近的结果。
相关文章:
【计算机视觉】轮廓检测
一、轮廓检测 在计算机视觉中,轮廓检测是另一个比较重要的任务,不单是用来检测图像或者视频帧中物体的轮廓,而且还有其他操作与轮廓检测相关。 以下代码展示了如何使用 OpenCV 进行 图像阈值处理、寻找图像轮廓 和 绘制轮廓 的完整流程&…...
【Linux】深入Linux:GCC/G++编译器实用指南
Linux相关知识点可以通过点击以下链接进行学习一起加油!初识指令指令进阶权限管理yum包管理与vim编辑器 在Linux系统中,理解和掌握GCC/G编译器是开发者不可或缺的技能之一。本文将深入探讨它们的工作原理和实际运用,帮助读者更好地利用这些强…...
【未来编程:AI如何通过合成复用原则优化设计】
🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:“没有罗马,那就自己创造罗马~” 文章目录 前言合成复用原则含义 继承复用含义UML图实现代码运行结果及分析优缺点 合成复用(我有这…...
【Rust自学】5.3. struct的方法(Method)
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 5.3.1. 什么是方法(Method) 方法和函数类似,也是用fn关键字进行声明,方法也有名称,也有参数ÿ…...
单片机 STM32入门
一、什么是单片机 单片机(Microcontroller Unit,MCU)是一种集成电路芯片,它将计算机的CPU、存储器(包括RAM和ROM)、输入/输出接口等集成在一个芯片上。单片机通常用于嵌入式系统,能够执行特定的…...
OneCode:开启高效编程新时代——企业定制出码手册
一、概述 OneCode 的 DSM(领域特定建模)出码模块是一个强大的工具,它支持多种建模方式,并具有强大的模型转换与集成能力,能够提升开发效率和代码质量,同时方便团队协作与知识传承,还具备方便的仿…...
学python还是学java?哪个相对来说比较容易上手?
在比较Python和Java哪个更容易上手时,可以从多个维度进行分析,包括语法简洁性、学习资源、应用领域、学习曲线等。 一、语法简洁性 Python:Python的语法简洁明了,更接近自然语言,易于理解和记忆。它使用缩进来表示代…...
C语言项目 天天酷跑(上篇)
前言 这里讲述这个天天酷跑是怎么实现的,我会在天天酷跑的下篇添加源代码,这里会讲述天天酷跑这个项目是如何实现的每一个思路,都是作者自己学习于别人的代码而创作的项目和思路,这个代码和网上有些许不一样,因为掺杂了…...
Windows 11 安装 Dify 完整指南 非docker环境
# Windows 11 安装 Dify 完整指南## 前置要求- Python 3.11 - Node.js 18 - PostgreSQL 14 - Redis for Windows - Git - Ollama (可选,用于本地模型)## 详细安装步骤### 1. 安装必要软件1. **Python 3.11**- 从 https://www.python.org/downloads/release/python-…...
MySQL变量
文章目录 MySQL变量系统变量查看系统变量设置系统变量 自定义变量用户变量局部变量 MySQL变量 MySQL变量分为系统变量和自定义变量 系统变量 系统变量有全局变量和会话变量 查看系统变量 #查看全局系统变量 show global variables; #根据条件查询全局系统变量 show global …...
Ubuntu离线安装Docker容器
前言 使用安装的工具snap安装在沙箱中,并且该沙箱之外的权限有限。docker无法从其隔离的沙箱环境访问外部文件系统。 目录 前言准备环境卸载已安装的Docker环境快照安装的Dockerapt删除Docker 安装docker-compose下载执行文件将文件移到 /usr/local/bin赋予执行权限…...
ensp 关于acl的运用和讲解
ACL(Access Control List,访问控制列表)是一种常用于网络设备(如路由器、交换机)上的安全机制,用于控制数据包的流动与访问权限。ACL 可以指定哪些数据包允许进入或离开某个网络接口,基于不同的…...
Linux(Centos 7.6)yum源配置
yum是rpm包的管理工具,可以自动安装、升级、删除软件包的功能,可以自动解决软件包之间的依赖关系,使得用户更方便软件包的管理。要使用yum必须要进行配置,个人将其分为三类,本地yum源、局域网yum源、第三方yum源&#…...
[WASAPI]音频API:从Qt MultipleMedia走到WASAPI,相似与不同
[WASAPI] 从Qt MultipleMedia 来看WASAPI 最近在学习有关Windows上的音频驱动相关的知识,在正式开始说WASAPI之前,我想先说一说Qt的Multiple Media,为什么呢?因为Qt的MultipleMedia实际上是WASAPI的一层封装,它在是线…...
什么是MVCC?
MVCC(多版本并发控制,Multi-Version Concurrency Control)是一种用于数据库管理系统中的并发控制的技术。它允许多个事务同时对同一数据进行读取和修改,而不会相互干扰,从而提高了数据库的并发性能。以下是对MVCC的详细…...
C/C++基础错题归纳
文章目录 第1天1.下面程序段的运行结果是:答案知识补充 2.当一个类A 中没有声明任何成员变量与成员函数,这时sizeof(A)的值是多少?答案知识补充 3.下面程序输出是什么?答案其他讲解 第1天 1.下面程序段的运行结果是: char C[5]{‘a’,’b’…...
Nginx 常用安全头
Web 应用中配置 HTTP 安全响应头是提升网站安全性的重要一步。合理配置 Nginx 的安全头,可以抵御常见的安全威胁(如 XSS、点击劫持、MIME 类型嗅探等),增强用户隐私保护和传输安全性。 常见的 HTTP 安全头及其作用 1. Content-Se…...
消息队列(一)消息队列的工作流程
什么是消息队列 首先,代入一个场景,我现在做一个多系统的集成,分别有系统A、B、C、D四个系统,A系统因为使用产生了业务数据,B、C、D需要使用这些数据做相关的业务处理和运算,最基本的做法就是通过接口通信…...
LeetCode 2605 从两个数字数组里生成最小数字
探寻两个数组数位关联下的最小数字问题 题目描述 给定两个只包含 1 到 9 之间数字的数组 nums1 和 nums2,并且每个数组中的元素都是互不相同的。我们需要返回最小的数字,要求这个数字满足两个数组都至少包含这个数字的某个数位。例如,若 nu…...
AI新书推荐:深度学习和大模型原理与实践(清华社)
本书简介 在这个信息爆炸、技术革新日新月异的时代,深度学习作为人工智能领域的重要分支,正引领着新一轮的技术革命。《深度学习和大模型原理与实践》一书,旨在为读者提供深度学习及其大模型技术的全面知识和实践应用的指南。 本书特色在于…...
32单片机串口数据接收、空闲IDLE中断详解
一、前提说明 一开始写单片机程序的时候不太清楚空闲中断这个东西,每次用串口接收数据,都要再开一个定时器,在定时器内进行倒计时,每次接收数据就重置计时时间,计时结束就触发中断,再判断所有接收的数据&am…...
WebRtc webrtc-streamer部署
文章目录 本文档只是为了留档方便以后工作运维,或者给同事分享文档内容比较简陋命令也不是特别全,不适合小白观看,如有不懂可以私信,上班期间都是在得 WebRtc webrtc-streamer 部署 docker run -p 8000:8000 -it mpromonet/webrt…...
shiro注入filter内存马(绕过长度限制)
shiro环境 https://github.com/yyhuni/shiroMemshell(实验环境) 这里用的 Client_memshell.java package com.example.demo;import javassist.ClassPool; import javassist.CtClass; import org.apache.shiro.crypto.AesCipherService; import org.ap…...
Springboot + vue3 实现大文件上传方案:秒传、断点续传、分片上传、前端异步上传
参考:https://juejin.cn/post/6870837414852886542#heading-9 一般计算大文件的md5都是前端来做,因为如果后端来做,那得等到上传成功后才能计算md5值,并且读取的时间也很长。 为了解决文件大传输慢的问题,前端可以通…...
渗透Vulnhub-DC-9靶机
本篇文章旨在为网络安全渗透测试行业靶机教学。通过阅读本文,读者将能够对渗透Vulnhub系列DC-6靶机有定的了解 一、信息收集阶段 DC-9靶场信息: DC-9靶场介绍: https://www.vulnhub.com/entry/dc-9,412/ DC-9靶场下载: https://download.vu…...
springboot477基于vue技术的农业设备租赁系统(论文+源码)_kaic
摘 要 使用旧方法对农业设备租赁系统的信息进行系统化管理已经不再让人们信赖了,把现在的网络信息技术运用在农业设备租赁系统的管理上面可以解决许多信息管理上面的难题,比如处理数据时间很长,数据存在错误不能及时纠正等问题。这次开发的农…...
CentOS常见命令
CentOS(Community ENTerprise Operating System)基于Red Hat Enterprise Linux(RHEL)源代码开发,是常用的Linux发行版之一。在CentOS系统中,有许多命令用于管理和操作系统,以下是一些CentOS系统…...
oracle 设置归档日志存放路径
oracle 设置归档日志存放路径 1、创建新目录 mkdir /archive chown -R oracle:oinstall /archive 注:条件允许的话,/archive 目录应独立挂载。1、便于监控目录使用率;2、避免和其它文件混淆,便于管理。 2、设置归档日志存放路…...
机器学习1-简单神经网络
相比传统的机器学习算法,深度学习做出了哪些改进呢?其实两者在理论结构上是一致的,即:模型假设、评价函数和优化算法,其根本差别在于假设的复杂度 构建简单神经网络(未训练): # 封装…...
C++的侵入式链表
非侵入式链表 非侵入式链表是一种链表数据结构,其中每个元素(节点)并不需要自己包含指向前后节点的指针。链表的结构和节点的存储是分开的,链表容器会单独管理这些指针。 常见的非侵入式链表节点可以由以下所示,即&a…...
MFC案例:图片文件转图标(ico)格式
本案例程序目的是将一般图像文件转换成图标格式(ico)。实现起来不是很复杂,这里为了介绍MFC的具体使用方法,在程序界面上分成几个功能块,包括:打开图像文件、选择ICON大小、转换、预览、保存等。相关具体步骤如下: 一、…...
【从零开始入门unity游戏开发之——unity篇02】unity6基础入门——软件下载安装、Unity Hub配置、安装unity编辑器、许可证管理
文章目录 一、软件下载安装1、Unity官网2、下载Unity Hub 二、修改Unity Hub配置1、设置Unity Hub中文语言2、修改默认存储目录 三、安装unity编辑器1、点击安装编辑器2、版本选择3、关于版本号4、安装模块选择5、等待下载完成自动安装即可6、追加unity和模块 四、许可证管理专…...
东子生物完成A轮战略融资,数字商品交易全新升级为数商时代
2024年11月23日,东子生物数字时代正式上线,标志着公司全面迈入“数商时代”,作为国内领先的生物科技企业,东子生物在数字化浪潮中精准布局,以创新科技推动产业升级,以全新的思维引领健康产业,兼…...
数据结构经典算法总复习(上卷)
第一章:数据结构导论 无重要考点,仅需了解时间复杂度。 第二章:线性表 1.获得线性表第i个元素 void GetElem_sq(SqList L, int i, ElemType &e) {if (i<1 || i>L.length) ErrorMsg("Invalid i value"); //注意错误监…...
电脑使用CDR时弹出错误“计算机丢失mfc140u.dll”是什么原因?“计算机丢失mfc140u.dll”要怎么解决?
电脑使用CDR时弹出“计算机丢失mfc140u.dll”错误:原因与解决方案 在日常电脑使用中,我们时常会遇到各种系统报错和文件丢失问题。特别是当我们使用某些特定软件,如CorelDRAW(简称CDR)时,可能会遇到“计算…...
oracle使用imp命令导入dmp文件
需求: 增量导入 tbl_servicelegalclause 表数据(dmp格式)。 导入思路:使用 dba 创建一个 临时库,先将 tbl_servicelegalclause.dmp(增量的数据) 文件导入到 临时库,然后确认临时库数…...
电脑出现 0x0000007f 蓝屏问题怎么办,参考以下方法尝试解决
电脑蓝屏是让许多用户头疼的问题,其中出现 “0x0000007f” 错误代码更是较为常见且棘手。了解其背后成因并掌握修复方法,能帮我们快速恢复电脑正常运行。 一、可能的硬件原因 内存问题 内存条长时间使用可能出现物理损坏,如金手指氧化、芯片…...
Logback日志框架中的继承机制详解
在Logback框架中,logger的继承机制是基于层级结构(hierarchical context)工作的。每个logger都被分配一个名称,这个名称可以看作是一个路径或目录结构,从而形成了一个逻辑上的树状结构。这种结构使得日志记录具有很强的…...
[Unity]【图形渲染】【游戏开发】Shader数学基础4-更多矢量运算
在计算机图形学和着色器编程中,矢量运算是核心的数学工具之一。矢量用于描述空间中的位置、方向、速度等各种物理量,并在图形变换、光照计算、纹理映射等方面起着至关重要的作用。本篇文章将详细讲解矢量和标量之间的乘法与除法、矢量的加法与减法、矢量的模与单位矢量、点积…...
node.js的异步工作之---回调函数与回调地狱
回调函数:在 Node.js 中,很多 API 都是异步的,通常通过回调函数来处理操作完成后的结果。这种回调模式虽然非常高效,但会导致代码逐渐变得难以维护,尤其是当有多个异步操作嵌套时(即回调地狱)。…...
tcp 的三次握手与四次挥手
问1: 请你说一下tcp的三次握手一次握手两次握手三次握手问: 为什么不四(更多)次握手? 问 2: 请说一下 tcp 的 4 次挥手一次挥手两次挥手问题:能不能等到数据传输完成再返回 ack? 三次挥手四次挥手问: 为什么要等两个最大报文存在时间? bg: tcp 是可靠的连接,如何保证 建立连…...
《三角洲行动》游戏运行时提示“缺失kernel32.dll”:问题解析与解决方案
《三角洲行动》游戏运行时提示“缺失kernel32.dll”:问题解析与解决方案 作为软件开发领域的一名从业者,我深知电脑游戏运行过程中可能遇到的各种挑战,尤其是文件丢失、文件损坏以及系统报错等问题。今天,我将以经典游戏《三角洲…...
Android——自定义按钮button
项目中经常高频使用按钮,要求:可设置颜色,有圆角且有按下效果的Button 一、自定义按钮button button的代码为 package com.fslihua.clickeffectimport android.annotation.SuppressLint import android.content.Context import android.gra…...
Pandas基础学习(1)
之前看的pandas的教材和课程里,内容参差不齐,实际使用很少的方法的内容有点多,导致很乱而且记不住那么多,这个帖子尽量用最少的文字,最精炼的语言来总结比较实用的方法,内容主要来源于《利用python进行数据…...
20241224在Ubuntu20.04.6下给X99平台上的M6000显卡安装驱动程序
20241224在Ubuntu20.04.6下给X99平台上的M6000显卡安装驱动程序 2024/12/24 16:18 下载驱动程序: https://www.nvidia.cn/drivers/lookup/ https://www.nvidia.cn/drivers/results/ https://www.nvidia.cn/drivers/details/237923/ https://www.nvidia.cn/drivers/l…...
批量多线程给TXT文档插入相关腾讯AI【高质量无水印无版权】原创图片
给蜘蛛访问的网站文章插入相关图片,可以带来以下好处: 1、提升用户体验:图片能够直观地展示文章内容,帮助用户更好地理解和消化信息。对于阅读者来说,图文并茂的内容往往更具吸引力,也能提高他们的阅读…...
保护模式基本概念
CPU 架构 RISC(Reduced Instruction Set Computer) 中文即"精简指令集计算机”。RISC构架的指令格式和长度通常是固定的(如ARM是32位的指令)、且指令和寻址方式少而简单、大多数指令在一个周期内就可以执行完毕 CISC&…...
mysql 查询优化之字段建立全文索引
最近在接手一些老项目时发现表设计存在问题导致查询较慢 例如一张旧表的设计: 模糊匹配某个关键字时,需要十几秒左右,而且表的数据量不多 都知道mysql8.0版本InnoDB引擎都支持全文索引了,因此可以在content建立全文索引,但全文索引对中文支持并不完善…...
redis和mysql的区别
Redis是一种非关系型数据库(NoSQL),将数据存储在缓存中,这虽然提高了运行效率,但是保存时间却很短。将数据存储在内存中,因此读写速度非常快,特别适合用于需要高速访问的场景,如缓存…...
【Laravel】接口的访问频率限制器
Laravel 接口的访问频率,你可以在 Laravel 中使用速率限制器(Rate Limiter)。以下是一个详细的步骤,展示如何为这个特定的 API 路由设置速率限制: 1. 配置 RouteServiceProvider 首先,确保在 App\Provide…...