File size: 4,104 Bytes
dd85fb6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aaa448c
dd85fb6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aaa448c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd85fb6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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