如何用Python OpenCV检测围棋棋盘网格并获取网格线端点?
围棋棋盘网格线合并与端点提取方案
针对HoughLinesP检测出大量碎线段的问题,核心思路是先分类横竖线,再聚类合并同方向的碎线段,最终得到完整的38条网格线(19横+19竖)并提取端点。
步骤1:提取并分类线段
先从HoughLinesP的输出中提取所有线段,再根据斜率区分横线(斜率接近0)和竖线(斜率接近无穷大):
import cv2 import numpy as np # 假设已完成边缘检测和HoughLinesP检测 # lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=80, maxLineGap=15) segments = [line[0] for line in lines] # 将(1411,1,4)转换为(1411,4)的线段列表 horizontal_segs = [] vertical_segs = [] for x1, y1, x2, y2 in segments: dx = x2 - x1 dy = y2 - y1 if dx == 0: vertical_segs.append((x1, y1, x2, y2)) continue slope = abs(dy / dx) # 根据斜率阈值分类,可根据实际图像调整阈值 if slope < 0.1: horizontal_segs.append((x1, y1, x2, y2)) elif slope > 10: vertical_segs.append((x1, y1, x2, y2))
步骤2:聚类合并同方向线段
利用K-means聚类(已知目标数量为19),将同一条网格线的碎线段归为一类,再合并得到完整线条的端点:
合并横线
from sklearn.cluster import KMeans # 提取每条横线线段的y坐标中心 h_centers_y = np.array([(y1 + y2) / 2 for x1, y1, x2, y2 in horizontal_segs]).reshape(-1, 1) # 聚类为19组(对应19条横线) kmeans_h = KMeans(n_clusters=19, random_state=0).fit(h_centers_y) # 按聚类标签分组线段 h_groups = {} for idx, label in enumerate(kmeans_h.labels_): if label not in h_groups: h_groups[label] = [] h_groups[label].append(horizontal_segs[idx]) # 合并每组线段,得到完整横线的左右端点 horizontal_lines = [] for group in h_groups.values(): all_x = [] all_y = [] for x1, y1, x2, y2 in group: all_x.extend([x1, x2]) all_y.extend([y1, y2]) min_x = min(all_x) max_x = max(all_x) avg_y = int(sum(all_y) / len(all_y)) # 横线端点:(左x, 平均y)、(右x, 平均y) horizontal_lines.append((min_x, avg_y, max_x, avg_y)) # 按y坐标排序,方便后续坐标系建立 horizontal_lines.sort(key=lambda l: l[1])
合并竖线
# 提取每条竖线线段的x坐标中心 v_centers_x = np.array([(x1 + x2) / 2 for x1, y1, x2, y2 in vertical_segs]).reshape(-1, 1) kmeans_v = KMeans(n_clusters=19, random_state=0).fit(v_centers_x) # 按聚类标签分组线段 v_groups = {} for idx, label in enumerate(kmeans_v.labels_): if label not in v_groups: v_groups[label] = [] v_groups[label].append(vertical_segs[idx]) # 合并每组线段,得到完整竖线的上下端点 vertical_lines = [] for group in v_groups.values(): all_x = [] all_y = [] for x1, y1, x2, y2 in group: all_x.extend([x1, x2]) all_y.extend([y1, y2]) min_y = min(all_y) max_y = max(all_y) avg_x = int(sum(all_x) / len(all_x)) # 竖线端点:(平均x, 上y)、(平均x, 下y) vertical_lines.append((avg_x, min_y, avg_x, max_y)) # 按x坐标排序 vertical_lines.sort(key=lambda l: l[0])
优化建议
- 预处理优化:在HoughLinesP检测前,对边缘图像做膨胀操作,减少碎线段数量:
edges = cv2.Canny(gray, 50, 150) kernel = np.ones((3,3), np.uint8) edges = cv2.dilate(edges, kernel, iterations=1) - HoughLinesP参数调整:增大
minLineLength过滤短线段,增大maxLineGap合并距离近的线段,减少后续聚类压力。
最终得到的horizontal_lines和vertical_lines各包含19条完整网格线,每条线的四个值就是对应端点坐标,可直接用于建立(x,y)坐标系。
内容的提问来源于stack exchange,提问作者Code-Apprentice
相关产品推荐
相关产品推荐

