如何在OpenCV中不受倾斜影响按棋盘格顺序排列Blob中心?
解决LED点阵Blob中心的稳定排序问题(不受倾斜影响)
针对大幅倾斜的LED点阵,直接按坐标x/y排序会失效,这里提供基于**主成分分析(PCA)**的稳定排序方案,能自动识别点阵的行/列方向,实现类似cv2.findChessboardCorners的棋盘格顺序排列。
核心思路
- 用PCA计算点阵的两个主方向:第一个主方向对应点阵的"行"延伸方向,第二个对应"列"延伸方向(可根据实际情况调整顺序)。
- 将每个Blob中心坐标投影到这两个主方向轴上,得到每个点在行、列方向的投影值。
- 先按行投影值分组(同一行的点投影值接近),再在每组内按列投影值排序,最终得到规则的棋盘格顺序。
完整代码实现
import cv2 import numpy as np from sklearn.decomposition import PCA from sklearn.cluster import KMeans def sort_led_points(points, grid_shape=None): """ 对LED点阵的Blob中心进行稳定排序,不受倾斜影响 :param points: 输入的Blob中心坐标数组,形状为(N, 2) :param grid_shape: 可选,点阵的网格形状(行数, 列数),已知则传入提升准确性 :return: 排序后的坐标数组,形状为(N, 2),顺序为从左到右、从上到下的棋盘格顺序 """ # 标准化坐标,消除平移影响 points_centered = points - np.mean(points, axis=0) # 执行PCA,获取点阵的主方向 pca = PCA(n_components=2) pca.fit(points_centered) principal_axes = pca.components_ # 将点投影到两个主方向轴上 proj_row = np.dot(points_centered, principal_axes[0]) proj_col = np.dot(points_centered, principal_axes[1]) # 分组逻辑:已知网格形状则直接拆分,未知则用KMeans聚类 if grid_shape is not None: num_rows, num_cols = grid_shape sorted_indices = np.argsort(proj_row) row_groups = np.array_split(sorted_indices, num_rows) else: # 假设点阵近似正方形,自动估算行数 num_rows = int(np.round(np.sqrt(len(points)))) kmeans = KMeans(n_clusters=num_rows, random_state=0) row_labels = kmeans.fit_predict(proj_row.reshape(-1, 1)) row_groups = [np.where(row_labels == i)[0] for i in range(num_rows)] # 对每个行组内的点按列投影值排序 sorted_points = [] for group in row_groups: group_proj_col = proj_col[group] group_sorted_indices = group[np.argsort(group_proj_col)] sorted_points.extend(points[group_sorted_indices]) return np.array(sorted_points, dtype=np.float32) # ---------------------- 调用示例 ---------------------- if __name__ == "__main__": # 原有Blob检测代码 img = cv2.imread('./image.png', cv2.IMREAD_GRAYSCALE) params = cv2.SimpleBlobDetector_Params() params.minThreshold = 50 params.maxThreshold = 255 params.filterByArea = True params.minArea = 0 params.maxArea = 80 params.filterByColor = True params.blobColor = 255 params.filterByCircularity = False params.filterByConvexity = False params.filterByInertia = False detector = cv2.SimpleBlobDetector_create(params) keypoints = detector.detect(img) # 提取Blob中心坐标 ips = np.array([(kp.pt[0], kp.pt[1]) for kp in keypoints], dtype=np.float32) # 排序:替换为你的点阵实际形状,比如(7,7),未知则省略grid_shape参数 sorted_ips = sort_led_points(ips, grid_shape=(7,7)) # 可选:绘制排序结果验证 img_color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) for i, (x, y) in enumerate(sorted_ips): cv2.circle(img_color, (int(x), int(y)), 3, (0, 255, 0), -1) cv2.putText(img_color, str(i), (int(x)+5, int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (0,0,255), 1) cv2.imwrite('sorted_leds.png', img_color)
关键说明
- PCA主方向识别:自动适配点阵的倾斜、旋转状态,无需手动调整方向参数。
- 分组灵活性:已知点阵行列数时传入
grid_shape可确保分组精准;未知时通过聚类自动估算行数,适配多数规则点阵场景。 - 无依赖手动参数:完全基于点阵的几何特征排序,避免了原方案中
shrinker这类需要手动调试的参数。
内容的提问来源于stack exchange,提问作者donggun
相关产品推荐
相关产品推荐

