Spaces:
Sleeping
Sleeping
| from typing import Any, Dict, Tuple | |
| import cv2 | |
| import numpy as np | |
| from .common import to_bgr, to_rgb | |
| def detect_classical( | |
| image: np.ndarray, | |
| detector: str, | |
| canny_low: int, | |
| canny_high: int, | |
| harris_k: float, | |
| harris_block: int, | |
| harris_ksize: int, | |
| hough_thresh: int, | |
| hough_min_len: int, | |
| hough_max_gap: int, | |
| ellipse_min_area: int, | |
| max_ellipses: int, | |
| line_detector: str = "hough", | |
| ) -> Tuple[np.ndarray, Dict[str, Any]]: | |
| bgr = to_bgr(image) | |
| gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY) | |
| overlay = bgr.copy() | |
| meta: Dict[str, Any] = {"path": "classical"} | |
| if detector == "Edges (Canny)": | |
| edges = cv2.Canny(gray, canny_low, canny_high, L2gradient=True) | |
| overlay[edges > 0] = (0, 255, 0) | |
| meta["num_edge_pixels"] = int(np.count_nonzero(edges)) | |
| elif detector == "Corners (Harris)": | |
| gray32 = np.float32(gray) | |
| dst = cv2.cornerHarris(gray32, blockSize=harris_block, ksize=harris_ksize, k=harris_k) | |
| dst = cv2.dilate(dst, None) | |
| thresh = 0.01 * dst.max() if dst.max() > 0 else 0.0 | |
| corners = np.argwhere(dst > thresh) | |
| for (y, x) in corners: | |
| cv2.circle(overlay, (int(x), int(y)), 2, (0, 255, 255), -1) | |
| meta["num_corners"] = int(len(corners)) | |
| elif detector == "Lines (Hough/LSD)": | |
| method = (line_detector or "hough").lower() | |
| if method not in {"hough", "lsd"}: | |
| method = "hough" | |
| meta["line_detector"] = method | |
| if method == "lsd": | |
| if not hasattr(cv2, "createLineSegmentDetector"): | |
| meta["error"] = "OpenCV build lacks Line Segment Detector (LSD) support." | |
| return to_rgb(overlay), meta | |
| lsd = cv2.createLineSegmentDetector(refine=cv2.LSD_REFINE_ADV) | |
| lines = lsd.detect(gray)[0] | |
| n = 0 | |
| if lines is not None: | |
| for seg in lines: | |
| x1, y1, x2, y2 = map(int, np.round(seg[0])) | |
| cv2.line(overlay, (x1, y1), (x2, y2), (0, 255, 255), 2) | |
| n = len(lines) | |
| meta["num_lines"] = int(n) | |
| else: | |
| edges = cv2.Canny(gray, canny_low, canny_high, L2gradient=True) | |
| lines = cv2.HoughLinesP( | |
| edges, | |
| rho=1, | |
| theta=np.pi / 180, | |
| threshold=hough_thresh, | |
| minLineLength=hough_min_len, | |
| maxLineGap=hough_max_gap, | |
| ) | |
| n = 0 | |
| if lines is not None: | |
| for l in lines: | |
| x1, y1, x2, y2 = l[0] | |
| cv2.line(overlay, (x1, y1), (x2, y2), (255, 128, 0), 2) | |
| n = len(lines) | |
| meta["num_lines"] = int(n) | |
| elif detector == "Ellipses (Contours + fitEllipse)": | |
| edges = cv2.Canny(gray, canny_low, canny_high, L2gradient=True) | |
| contours, _ = cv2.findContours(edges, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE) | |
| ellipses = [] | |
| for cnt in contours: | |
| if len(cnt) < 5: | |
| continue | |
| try: | |
| (cx, cy), (MA, ma), angle = cv2.fitEllipse(cnt) | |
| area = float(np.pi * (MA / 2) * (ma / 2)) | |
| if area >= ellipse_min_area: | |
| ellipses.append(((cx, cy), (MA, ma), angle, area)) | |
| except cv2.error: | |
| continue | |
| ellipses.sort(key=lambda e: e[3], reverse=True) | |
| kept = [] | |
| for e in ellipses: | |
| if len(kept) >= max_ellipses: | |
| break | |
| (cx, cy), (MA, ma), angle, area = e | |
| if all((cx - kx) ** 2 + (cy - ky) ** 2 > 100 for ((kx, ky), _, _, _) in kept): | |
| kept.append(e) | |
| for (cx, cy), (MA, ma), angle, area in kept: | |
| cv2.ellipse(overlay, ((int(cx), int(cy)), (int(MA), int(ma)), float(angle)), (0, 200, 255), 2) | |
| cv2.circle(overlay, (int(cx), int(cy)), 2, (0, 200, 255), -1) | |
| meta["num_ellipses"] = int(len(kept)) | |
| else: | |
| meta["error"] = f"Unknown detector: {detector}" | |
| return to_rgb(overlay), meta | |