Spaces:
Sleeping
Sleeping
| import cv2 | |
| import numpy as np | |
| from typing import List, Tuple, Dict, Literal, TypedDict, Any | |
| class BoundingBoxDict(TypedDict): | |
| bbox: Tuple[int, int, int, int] | |
| confidence: float | |
| def multiscale_template_match( | |
| drawing_gray: np.ndarray, | |
| template_preprocessed: np.ndarray, | |
| scale_range: Tuple[float, float] = (0.5, 1.5), | |
| scale_step: float = 0.05, | |
| threshold: float = 0.50, | |
| cancellation_state: Any = None, | |
| ) -> List[Tuple[int, int, int, int, float, float]]: | |
| """ | |
| Khớp mẫu đa tỷ lệ sử dụng NCC chuẩn hóa Pearson bất biến ánh sáng. | |
| """ | |
| num_steps = int(round((scale_range[1] - scale_range[0]) / scale_step)) + 1 | |
| scales = np.linspace(scale_range[0], scale_range[1], num_steps) | |
| all_boxes = [] | |
| th_h, th_w = template_preprocessed.shape[:2] | |
| dh, dw = drawing_gray.shape[:2] | |
| for scale in scales: | |
| if cancellation_state is not None: | |
| cancellation_state.check() | |
| new_w = max(int(th_w * scale), 5) | |
| new_h = max(int(th_h * scale), 5) | |
| if new_h > dh or new_w > dw: | |
| continue | |
| interp = cv2.INTER_AREA if scale < 1.0 else cv2.INTER_LINEAR | |
| resized_tmpl = cv2.resize( | |
| template_preprocessed, (new_w, new_h), interpolation=interp | |
| ) | |
| result = cv2.matchTemplate(drawing_gray, resized_tmpl, cv2.TM_CCOEFF_NORMED) | |
| locs = np.where(result >= threshold) | |
| for (y, x) in zip(*locs): | |
| score = float(result[y, x]) | |
| all_boxes.append((int(x), int(y), int(new_w), int(new_h), score, float(scale))) | |
| return all_boxes | |
| def _compute_iou( | |
| bbox_a: Tuple[int, int, int, int], | |
| bbox_b: Tuple[int, int, int, int], | |
| ) -> float: | |
| """Intersection over Union.""" | |
| ax, ay, aw, ah = bbox_a | |
| bx, by, bw, bh = bbox_b | |
| inter_x1 = max(ax, bx) | |
| inter_y1 = max(ay, by) | |
| inter_x2 = min(ax + aw, bx + bw) | |
| inter_y2 = min(ay + ah, by + bh) | |
| if inter_x2 <= inter_x1 or inter_y2 <= inter_y1: | |
| return 0.0 | |
| inter_area = (inter_x2 - inter_x1) * (inter_y2 - inter_y1) | |
| union_area = aw * ah + bw * bh - inter_area | |
| return inter_area / union_area if union_area > 0 else 0.0 | |
| def soft_nms( | |
| boxes: List[BoundingBoxDict], | |
| iou_threshold: float = 0.3, | |
| sigma: float = 0.5, | |
| score_threshold: float = 0.3, | |
| method: Literal["linear", "gaussian"] = "gaussian", | |
| ) -> List[BoundingBoxDict]: | |
| """ | |
| Soft-NMS làm giảm dần confidence score của các hộp chồng lấp lớn để hỗ trợ sub-patterns. | |
| """ | |
| if not boxes: | |
| return [] | |
| boxes = [b.copy() for b in boxes] | |
| result = [] | |
| while boxes: | |
| best_idx = max(range(len(boxes)), key=lambda i: boxes[i]["confidence"]) | |
| best = boxes.pop(best_idx) | |
| result.append(best) | |
| remaining = [] | |
| for box in boxes: | |
| iou = _compute_iou(best["bbox"], box["bbox"]) | |
| if method == "gaussian": | |
| box["confidence"] *= float(np.exp(-(iou ** 2) / sigma)) | |
| elif method == "linear" and iou > iou_threshold: | |
| box["confidence"] *= float(1.0 - iou) | |
| if box["confidence"] >= score_threshold: | |
| remaining.append(box) | |
| boxes = remaining | |
| return result | |
| def refine_bbox_local_search( | |
| drawing: np.ndarray, | |
| bbox: Tuple[int, int, int, int], | |
| template_processed: np.ndarray, | |
| search_radius: int = 8, | |
| ) -> Tuple[int, int, int, int, float]: | |
| """ | |
| Quét tinh chỉnh cục bộ ±search_radius px để chỉnh tọa độ BBox lệch tối đa. | |
| """ | |
| x, y, w, h = bbox | |
| H, W = drawing.shape[:2] | |
| best_score = -1.0 | |
| best_bbox = bbox | |
| interp = cv2.INTER_AREA if (w < template_processed.shape[1]) else cv2.INTER_LINEAR | |
| tmpl_resized = cv2.resize(template_processed, (w, h), interpolation=interp) | |
| for dy in range(-search_radius, search_radius + 1): | |
| for dx in range(-search_radius, search_radius + 1): | |
| nx, ny = x + dx, y + dy | |
| if nx < 0 or ny < 0 or nx + w > W or ny + h > H: | |
| continue | |
| patch = drawing[ny : ny + h, nx : nx + w] | |
| if patch.shape[0] != h or patch.shape[1] != w: | |
| continue | |
| res = cv2.matchTemplate(patch, tmpl_resized, cv2.TM_CCOEFF_NORMED) | |
| score = float(res[0, 0]) | |
| if score > best_score: | |
| best_score = score | |
| best_bbox = (nx, ny, w, h) | |
| return (*best_bbox, best_score) | |