| import numpy as np |
| import cv2 |
|
|
| |
| |
| |
| COURT_KEYPOINTS = { |
| 0: "left_outer_top", |
| 1: "left_baseline_upper_inner", |
| 2: "left_paint_outer_top", |
| 3: "left_paint_outer_bottom", |
| 4: "left_baseline_lower_inner", |
| 5: "left_outer_bottom", |
|
|
| 6: "left_paint_inner_top", |
| 7: "left_paint_inner_bottom", |
|
|
| 8: "midline_top", |
| 9: "midline_bottom", |
|
|
| 10: "right_outer_top", |
| 11: "right_baseline_upper_inner", |
| 12: "right_paint_outer_top", |
| 13: "right_paint_outer_bottom", |
| 14: "right_baseline_lower_inner", |
| 15: "right_outer_bottom", |
|
|
| 16: "right_paint_inner_top", |
| 17: "right_paint_inner_bottom", |
| } |
|
|
| |
| |
| TACTICAL_POINTS = { |
| 0: (0, 0), |
| 1: (0, 8), |
| 2: (0, 18), |
| 3: (0, 32), |
| 4: (0, 42), |
| 5: (0, 50), |
|
|
| 6: (18, 18), |
| 7: (18, 32), |
|
|
| 8: (47, 0), |
| 9: (47, 50), |
|
|
| 10: (94, 0), |
| 11: (94, 8), |
| 12: (94, 18), |
| 13: (94, 32), |
| 14: (94, 42), |
| 15: (94, 50), |
|
|
| 16: (76, 18), |
| 17: (76, 32), |
| } |
|
|
| |
| COURT_EDGES = [ |
| (0, 8), (8, 10), |
| (5, 9), (9, 15), |
| (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), |
| (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), |
| (8, 9), |
| (2, 6), (6, 7), (7, 3), |
| (12, 16), (16, 17), (17, 13), |
| ] |
|
|
| def validate_court_keypoints(points_dict, conf_threshold=0.3): |
| """ |
| Validate the geometric structure of detected court keypoints. |
| points_dict: dict[int, dict] where each dict has 'x', 'y', 'conf' |
| """ |
| errors = [] |
| |
| |
| valid_points = {k: (v['x'], v['y']) for k, v in points_dict.items() if v.get('conf', 1.0) >= conf_threshold} |
| |
| |
| if len(valid_points) < 4: |
| errors.append("Insufficient points for homography (minimum 4 required)") |
|
|
| |
| if all(k in valid_points for k in [0, 8, 10]): |
| if not (valid_points[0][0] < valid_points[8][0] < valid_points[10][0]): |
| errors.append("Top boundary x-order mismatch (Left < Mid < Right)") |
|
|
| if all(k in valid_points for k in [5, 9, 15]): |
| if not (valid_points[5][0] < valid_points[9][0] < valid_points[15][0]): |
| errors.append("Bottom boundary x-order mismatch (Left < Mid < Right)") |
|
|
| |
| if all(k in valid_points for k in [8, 9]): |
| if not (valid_points[8][1] < valid_points[9][1]): |
| errors.append("Midline vertical order mismatch (Top should be above Bottom)") |
|
|
| |
| if all(k in valid_points for k in [2, 6]): |
| if not (valid_points[2][0] < valid_points[6][0]): |
| errors.append("Left paint width invalid (Outer should be left of Inner)") |
|
|
| if all(k in valid_points for k in [12, 16]): |
| if not (valid_points[16][0] < valid_points[12][0]): |
| errors.append("Right paint width invalid (Inner should be left of Outer)") |
|
|
| return errors, valid_points |
|
|
| def get_homography_points(points_dict, conf_threshold=0.3): |
| """ |
| Collect valid source and destination points for homography calculation. |
| Returns (src_pts, dst_pts) as numpy arrays. |
| """ |
| _, valid_points = validate_court_keypoints(points_dict, conf_threshold) |
| |
| src_list = [] |
| dst_list = [] |
| |
| for idx, (x, y) in valid_points.items(): |
| if idx in TACTICAL_POINTS: |
| src_list.append([x, y]) |
| dst_list.append(TACTICAL_POINTS[idx]) |
| |
| if len(src_list) < 4: |
| return None, None |
| |
| return np.array(src_list, dtype=np.float32), np.array(dst_list, dtype=np.float32) |
|
|