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

如何使用OpenCV/Python连接二值图像中的断裂线条

断裂平行线条的连接方案

断裂的线条

图像中平行线条存在断裂,常规形态学操作连接效果不佳,由于线条方向一致,单靠方向计算无法解决问题。以下是几种可行的Python实现方案:

用户当前代码(补全缺失导入)

import cv2
import numpy as np
from math import atan2, sqrt, pi

img = cv2.imread('mask.jpg')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 转换为二值图像
_, bw = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 计算轮廓
contours, _ = cv2.findContours(bw, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

def get_orientation(pts, img):
    sz = len(pts)
    data_pts = np.empty((sz, 2), dtype=np.float64)
    for i in range(data_pts.shape[0]):
        data_pts[i,0] = pts[i,0,0]
        data_pts[i,1] = pts[i,0,1]
    # 执行PCA分析
    mean = np.empty((0))
    mean, eigenvectors, eigenvalues = cv2.PCACompute2(data_pts, mean)
    # 存储目标中心
    cntr = (int(mean[0,0]), int(mean[0,1]))
    cv2.circle(img, cntr, 3, (255, 0, 255), 2)
    p1 = (cntr[0] + 0.02 * eigenvectors[0,0] * eigenvalues[0,0], cntr[1] + 0.02 *  eigenvectors[0,1] * eigenvalues[0,0])
    p2 = (cntr[0] - 0.02 * eigenvectors[1,0] * eigenvalues[1,0], cntr[1] - 0.02 * eigenvectors[1,1] * eigenvalues[1,0])
    draw_axis(img, cntr, p1, (0, 150, 0), 1)
    draw_axis(img, cntr, p2, (200, 150, 0), 5)
    angle = atan2(eigenvectors[0,1], eigenvectors[0,0]) # 弧度制方向角
    return angle

def draw_axis(img, p_, q_, colour, scale):
    p = list(p_)
    q = list(q_)
    angle = atan2(p[1] - q[1], p[0] - q[0]) # 弧度制角度
    hypotenuse = sqrt((p[1] - q[1]) * (p[1] - q[1]) + (p[0] - q[0]) * (p[0] - q[0]))
    # 按比例延长箭头
    q[0] = p[0] - scale * hypotenuse * cos(angle)
    q[1] = p[1] - scale * hypotenuse * sin(angle)
    cv2.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv2.LINE_AA)
    # 绘制箭头钩
    p[0] = q[0] + 9 * cos(angle + pi / 4)
    p[1] = q[1] + 9 * sin(angle + pi / 4)
    cv2.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv2.LINE_AA)
    p[0] = q[0] + 9 * cos(angle - pi / 4)
    p[1] = q[1] + 9 * sin(angle - pi / 4)
    cv2.line(img, (int(p[0]), int(p[1])), (int(q[0]), int(q[1])), colour, 1, cv2.LINE_AA)

for i,c in enumerate(contours):
  # 每个轮廓的面积
  area = cv2.contourArea(c)
  # 计算每个形状的方向
  orrr = get_orientation(c,img)
  print(orrr)

方案1:定向膨胀(自定义结构元素)

通过定制方向匹配的结构元素,实现仅在线条延伸方向上的膨胀,避免无差别扩张:

水平线条场景

import cv2
import numpy as np

img = cv2.imread('mask.jpg', 0)
_, bw = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 创建水平结构元素:长度根据断裂间隙调整,宽度保持与线条一致
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 1))
# 定向膨胀连接断裂
dilated = cv2.dilate(bw, kernel, iterations=1)
# 可选:腐蚀还原线条粗细(避免过度膨胀)
eroded = cv2.erode(dilated, cv2.getStructuringElement(cv2.MORPH_RECT, (1,1)), iterations=1)

cv2.imshow('Connected Lines', eroded)
cv2.waitKey(0)
cv2.destroyAllWindows()

倾斜线条场景

如果线条是倾斜的,旋转结构元素适配方向:

import cv2
import numpy as np

img = cv2.imread('mask.jpg', 0)
_, bw = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 定义结构元素尺寸与旋转角度
kernel_size = (15, 3)
angle = 45  # 替换为实际线条角度

# 创建矩形核并旋转
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
center = (kernel_size[0]//2, kernel_size[1]//2)
rot_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
kernel = cv2.warpAffine(kernel, rot_matrix, kernel_size, flags=cv2.INTER_NEAREST)

# 定向膨胀
dilated = cv2.dilate(bw, kernel, iterations=1)

cv2.imshow('Rotated Dilated Lines', dilated)
cv2.waitKey(0)
cv2.destroyAllWindows()

方案2:霍夫直线检测重绘

利用霍夫直线检测识别平行线条的整体走向,直接绘制完整线条:

import cv2
import numpy as np

img = cv2.imread('mask.jpg', 0)
_, bw = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 边缘检测提升霍夫检测精度
edges = cv2.Canny(bw, 50, 150, apertureSize=3)
# 霍夫直线检测:阈值根据图像调整
lines = cv2.HoughLines(edges, 1, np.pi/180, threshold=100)

result = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR)
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))
    cv2.line(result, (x1, y1), (x2, y2), (0, 255, 0), 2)

cv2.imshow('Hough Connected Lines', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

方案3:端点配对连接

提取所有线段端点,匹配同方向近距离的端点并连接:

import cv2
import numpy as np
from math import hypot

img = cv2.imread('mask.jpg', 0)
_, bw = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
contours, _ = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 提取所有轮廓的端点
endpoints = []
for cnt in contours:
    pt1 = cnt[0][0]
    pt2 = cnt[-1][0]
    endpoints.append((pt1[0], pt1[1]))
    endpoints.append((pt2[0], pt2[1]))

result = cv2.cvtColor(bw, cv2.COLOR_GRAY2BGR)
threshold_dist = 20  # 间隙阈值,根据实际情况调整
y_tolerance = 5  # 平行线条的y坐标容忍度

visited = [False] * len(endpoints)
for i in range(len(endpoints)):
    if visited[i]:
        continue
    x1, y1 = endpoints[i]
    min_dist = float('inf')
    match_idx = -1
    # 寻找同方向近距离端点
    for j in range(i+1, len(endpoints)):
        if visited[j]:
            continue
        x2, y2 = endpoints[j]
        dist = hypot(x2 - x1, y2 - y1)
        if abs(y2 - y1) < y_tolerance and dist < min_dist and dist < threshold_dist:
            min_dist = dist
            match_idx = j
    # 连接配对端点
    if match_idx != -1:
        cv2.line(result, (x1, y1), endpoints[match_idx], (0, 0, 255), 2)
        visited[i] = True
        visited[match_idx] = True

cv2.imshow('Endpoint Connected Lines', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 03:10:16