You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提取视频中电缆轮廓的中心线并记录其点集?

电缆轮廓中心线提取方案

问题描述

需要为视频中的电缆轮廓绘制中心线,并将中心线的点记录到数组中。现有基于OpenCV的代码已完成电缆轮廓检测,但此前尝试将轮廓点拆分为上下两部分取均值生成中心线的方法,因视频中电缆运动导致轮廓上下部分点数量不均,结果极不准确且不完整,需生成贯穿轮廓中间的准确中心线。

现有代码

import cv2
from PIL import Image
from function import get_limits, get_limits_black
import numpy as np

black = [0,0,0]
cap = cv2.VideoCapture("/Users/samayjain/PycharmProjects/PythonProject/2025-06-07 11-24-40 Side.mp4")

while True:
    ret, frame = cap.read()
    roi = frame[0:1500, 0:1400]
    hsvImage = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    lowerBlack, upperBlack = get_limits_black(color=black)
    mask_black = cv2.inRange(hsvImage, lowerBlack, upperBlack)

    contours, _ = cv2.findContours(mask_black, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    i = 0
    max_width = 0
    index = 0
    c_x,c_y,c_w,c_h= 0,0,0,0
    for contour in contours:
        min_area = 1000
        if cv2.contourArea(contour) > min_area:
            x, y, w, h = cv2.boundingRect(contour)
            if x + w > max_width:
                index = i
                max_width = x + w
                c_x,c_y,c_w,c_h=x,y,w,h
                M = cv2.moments(contour)
                if M["m00"] != 0:
                    centroid_x = int(M["m10"] / M["m00"])
                    centroid_y = int(M["m01"] / M["m00"])
        i += 1
    cv2.rectangle(frame, (c_x, c_y), (c_x + c_w, c_y + c_h), (0, 255, 0), 2)
    cv2.drawContours(frame, contours, index, (0, 255, 0), 2)

    cv2.circle(frame, (centroid_x, centroid_y), 10, (0, 0, 255), -1)
    cv2.imshow("Original Frame with Contours", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

解决方案

针对电缆这类长条状轮廓,推荐两种稳定的中心线提取方法:

方法1:水平扫描线取中点

遍历轮廓包围盒内的每一行(y坐标),找到该行中属于电缆区域的左右边界,取两者中点作为中心线的点。该方法不受轮廓点数量影响,结果稳定。

方法2:骨架提取(形态学细化)

对电缆的二值掩码进行形态学细化操作,得到轮廓的骨架,骨架即为中心线。需注意预处理去除噪声,避免骨架出现分支。

以下是整合了水平扫描线法的修改代码,同时记录中心线点到数组:

import cv2
from function import get_limits_black
import numpy as np

black = [0,0,0]
cap = cv2.VideoCapture("/Users/samayjain/PycharmProjects/PythonProject/2025-06-07 11-24-40 Side.mp4")

# 存储所有帧的中心线点
all_centerline_points = []

while True:
    ret, frame = cap.read()
    if not ret:
        break  # 视频读取完毕退出
    
    roi = frame[0:1500, 0:1400]
    hsvImage = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
    lowerBlack, upperBlack = get_limits_black(color=black)
    mask_black = cv2.inRange(hsvImage, lowerBlack, upperBlack)

    contours, _ = cv2.findContours(mask_black, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    target_contour = None
    max_width = 0
    c_x,c_y,c_w,c_h= 0,0,0,0

    # 筛选目标电缆轮廓(最宽的大轮廓)
    for contour in contours:
        if cv2.contourArea(contour) > 1000:
            x, y, w, h = cv2.boundingRect(contour)
            if x + w > max_width:
                max_width = x + w
                c_x,c_y,c_w,c_h = x,y,w,h
                target_contour = contour

    if target_contour is not None:
        # 创建仅包含目标轮廓的掩码
        contour_mask = np.zeros_like(mask_black)
        cv2.drawContours(contour_mask, [target_contour], -1, 255, thickness=cv2.FILLED)
        
        centerline_points = []
        # 遍历包围盒内的每一行
        for y in range(c_y, c_y + c_h):
            # 获取当前行的所有白色像素列坐标
            cols = np.where(contour_mask[y, c_x:c_x+c_w] == 255)[0]
            if len(cols) >= 2:
                left = cols[0] + c_x
                right = cols[-1] + c_x
                mid_x = (left + right) // 2
                centerline_points.append((mid_x, y))
        
        # 将当前帧的中心线点存入总数组
        all_centerline_points.append(centerline_points)

        # 绘制中心线
        for (x, y) in centerline_points:
            cv2.circle(frame, (x, y), 2, (0, 0, 255), -1)

    # 绘制轮廓和包围盒
    cv2.rectangle(frame, (c_x, c_y), (c_x + c_w, c_y + c_h), (0, 255, 0), 2)
    if target_contour is not None:
        cv2.drawContours(frame, [target_contour], -1, (0, 255, 0), 2)

    cv2.imshow("Centerline Result", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

# 可打印查看某帧的中心线点示例
if all_centerline_points:
    print("第一帧中心线点数量:", len(all_centerline_points[0]))
    print("部分点示例:", all_centerline_points[0][:5])

代码说明

  1. 轮廓筛选:保留面积大于1000的轮廓,并选择最宽的作为目标电缆轮廓,避免干扰。
  2. 单独掩码:为目标轮廓创建独立掩码,确保只处理电缆区域。
  3. 扫描线取中点:对每一行的电缆区域左右边界取中,得到连续的中心线点,存入数组。
  4. 可视化:将中心线用红色圆点绘制在帧上,方便验证效果。

内容的提问来源于stack exchange,提问作者turnip

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 17:07:11