当前位置: 首页 > article >正文

【计算机视觉】轮廓检测

一、轮廓检测

在计算机视觉中,轮廓检测是另一个比较重要的任务,不单是用来检测图像或者视频帧中物体的轮廓,而且还有其他操作与轮廓检测相关。
以下代码展示了如何使用 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运行效果

  1. 原始图像:显示加载的灰度图。
  2. 二值化图像:经过阈值处理后的二值图像,轮廓清晰可见。
  3. 轮廓绘制:在彩色图像上绘制轮廓线。
    在这里插入图片描述

二、查找最小闭圆轮廓案例

找到一个正方形轮廓很简单,要找到到不规则的,歪斜的以及旋转的形状,可以用OpenCVcv2.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运行效果

  1. 绿色矩形框
    • 传统的矩形边界框,紧紧包围住轮廓。
  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运行效果

  1. 绿色轮廓:显示原始轮廓。
  2. 红色凸包:显示轮廓的最小凸多边形。
  3. 凸性缺陷(如果启用扩展):
    • 黄色圆点表示凹陷点。
    • 蓝线表示凹陷区域的边界。

四、直线和圆检测

4.1直线检测

OpenCV 中的 HoughLinesHoughLinesP 是常用的直线检测方法,分别用于检测 标准直线概率直线。以下是这两种方法的详细介绍和代码示例

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. 参数调节指南

影响结果的关键参数
  1. param1param2
    • param1:控制 Canny 边缘检测的敏感度。一般选 50-200 之间。
    • param2:累加器的阈值。值越大,检测到的圆越少,但更精准。一般选择 20-100 之间。
  2. minDist
    • 圆之间的最小距离。如果过小,可能会导致重复检测;过大则可能漏检。
  3. minRadiusmaxRadius
    • 指定圆的半径范围,设置合适值可以提高检测效率和准确率。

4. 示例运行结果

假设输入图像包含多个圆,运行结果将:

  1. 在每个检测到的圆上绘制一个绿色的边界。
  2. 在每个圆心位置绘制一个红色的小点。

5. 实用场景

  • 检测硬币、球体、瞳孔等圆形物体。
  • 工业检测中用于测量零件的圆形特征。
  • 交通领域中用于检测圆形标志。

6. 注意事项

  1. 输入图像需为灰度图像
    • 如果输入为彩色图像,需先转换为灰度图:
      gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
      
  2. 预处理图像
    • 在检测前,使用高斯模糊减少噪声:
      gray = cv.GaussianBlur(gray, (9, 9), 2)
      
  3. 参数调节
    • 参数需要针对具体场景多次调整,尤其是 param1param2

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. 检测图像中的椭圆。
  2. 利用椭圆的外接圆定义一个校正区域。
  3. 对椭圆进行透视变换,将其校正为圆形,并定位圆心位置。

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_axisminor_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 中,可以使用轮廓检测方法对图像中的任意多边形进行检测。

实现步骤

  1. 读取图像并预处理

    • 转换为灰度图像。
    • 对图像进行二值化或边缘检测(如 Canny 算法)。
  2. 检测轮廓

    • 使用 cv2.findContours 方法提取图像中的轮廓。
  3. 轮廓逼近

    • 使用 cv2.approxPolyDP 对轮廓点进行逼近,从而提取多边形。
  4. 可视化多边形

    • 绘制检测到的多边形。

代码示例

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()

关键方法解析

  1. cv2.findContours

    • 用于提取二值图像的轮廓。
    • 参数:
      • cv2.RETR_TREE:提取所有轮廓并构建层级关系。
      • cv2.CHAIN_APPROX_SIMPLE:存储轮廓关键点,压缩冗余点。
  2. cv2.approxPolyDP

    • 用于将轮廓逼近为多边形。
    • 参数:
      • epsilon:逼近精度,值为轮廓周长的一个比例。
      • True:表示轮廓是否封闭。
  3. cv2.arcLength

    • 用于计算轮廓的周长,帮助确定 epsilon

调整参数

  • epsilon
    • 越小,多边形越接近原始轮廓(更精确)。
    • 越大,顶点数越少(近似程度更高)。
  • 根据图像复杂程度适当调整 cv2.Canny 的阈值或二值化参数。

检测效果

  • 对于规则图形(如矩形、三角形等),可以准确检测顶点。
  • 对于不规则形状,输出的多边形是逼近的结果。

http://www.kler.cn/a/451850.html

相关文章:

  • adb无法连接到安卓设备【解决方案】报错:adb server version (40) doesn‘t match this client (41);
  • Django 模型中使用选择(choices):全面指南
  • C++----------类的设计
  • React第十八节 useEffect 用法使用技巧注意事项详解
  • vscode插件更新特别慢的问题
  • 第十五章 C++ 数组
  • ArcGIS经纬度转平面坐标教程
  • 第3章 集合与关系
  • 重温设计模式-外观模式和适配器模式的异同
  • YOLOv9-0.1部分代码阅读笔记-metrics.py
  • 数据结构:链表(经典算法例题)详解
  • 16 网络流
  • 【AIGC-ChatGPT副业提示词指令】炼金术士的元素启示:在神秘中寻找生命的答案【限时免费阅读,一天之后自动进入进阶课程】
  • Jenkins集成部署(图文教程、超级详细)
  • 【每日学点鸿蒙知识】蓝牙Key、页面元素层级工具、私仓搭建、锁屏显示横幅、app安装到真机
  • 基于Spring Boot的网络购物商城的设计与实现
  • 软件测试之测试用例
  • 突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
  • 基于LR/GNB/SVM/KNN/DT算法的鸢尾花分类和K-Means算法的聚类分析
  • SpringBoot从入门到实战:动态解析MyBatis SQL字符串获取可执行的SQL
  • 深度学习的DataLoader是什么数据类型,为什么不可用来索引
  • python中bug修复案例-----图形界面程序中修复bug
  • Python数字图像处理课程平台的开发
  • WPS怎么都无法删除空白页_插入空白页一次插入两张?_插入横屏空白页_横屏摆放图片_这样解决_显示隐藏段落标记---WPS工作笔记001
  • 【多时段】含sop的配电网重构【含分布式电源】【已更新视频讲解】
  • “协同过滤技术实战”:网上书城系统的设计与实现