5.27. Wyszukiwanie okręgów i prostokątów¶
Linie i segmenty obejmują proste krawędzie w przechwyconej ramce, ale wiele rzeczywistych cech, których szuka kamera, nie jest prostych. Moneta leżąca na biurku to okrąg. Wydrukowana etykieta, karteczka samoprzylepna czy górna powierzchnia pudełka oglądana pod kątem to czworokąt. Moduł image udostępnia dedykowany detektor dla każdego z nich: wyszukiwanie okręgów w stylu Hougha oraz wyszukiwanie kształtów czterobocznych wywodzące się z AprilTag.
Obie metody działają według tego samego schematu co detektory linii – threshold kontroluje, ile głosów potrzebuje wykrycie, roi zawęża obszar wyszukiwania, a zwracane obiekty niosą zarówno pozycję, jak i wielkość pewności – jednak przestrzenie parametrów i właściwe wartości domyślne różnią się na tyle, że zasługują na osobne omówienie.
5.27.1. Okręgi Hougha¶
find_circles() uruchamia okręgowy wariant transformaty Hougha. Każdy piksel krawędzi z wstępnego przebiegu Sobela głosuje na każdy okrąg, który mógłby przez niego przechodzić; zwracane są okręgi, które zbiorą wystarczająco dużo głosów.
circles = img.find_circles(threshold=3500,
x_margin=10, y_margin=10, r_margin=10,
r_min=10, r_max=80, r_step=2)
for c in circles:
img.draw_circle((c.x, c.y, c.r), color=(255, 0, 0))
threshold to minimalna suma wielkości krawędzi Sobela wzdłuż kandydującego okręgu. Większe okręgi wyznaczają więcej pikseli, więc potrzebują wyższych progów, aby przejść; wartość, która znajduje monetę o promieniu 20 pikseli, zadziała również na szum wokół krawędzi o promieniu 100 pikseli, podczas gdy wartość dostrojona do dużej monety pominie tę małą. Gdy znany jest zakres docelowego promienia, właściwy próg skaluje się z obwodem – z grubsza threshold = 50 * 2 * pi * r daje rozsądny punkt wyjścia, a właściwa wartość wynika z krótkiego przebiegu dostrajającego.
r_min, r_max i r_step ustawiają wyszukiwanie promienia. Bez ograniczeń detektor przeszukiwałby każdy promień od kilku pikseli aż do połowy szerokości obrazu, co jest zarówno powolne, jak i przepisem na fałszywe wykrycia. Ustawienie r_min i r_max tak, aby z dużym zapasem obejmowały oczekiwany rozmiar celu (np. r_min=15, r_max=25 dla monety, o której wiadomo, że ma około 20 pikseli), znacznie ogranicza pracę i poprawia stosunek sygnału do szumu głosów. r_step kontroluje ziarnistość wyszukiwania; większe kroki działają szybciej i mogą pominąć okrąg, którego rzeczywisty promień przypada między dwiema próbkowanymi wartościami. Domyślne r_step=2 to rozsądny kompromis.
x_margin, y_margin i r_margin kontrolują łączenie pobliskich wykryć, tak jak theta_margin i rho_margin robią to przy wykrywaniu linii. Pojedynczy fizyczny okrąg na obrazie głosuje na klaster kandydujących okręgów, których środki i promienie zgadzają się z dokładnością do kilku pikseli; marginesy zwijają każdy klaster do jego szczytu, zanim zbudowana zostanie lista wyników. Większe marginesy zwracają mniej, bardziej pewnych wykryć; mniejsze marginesy zwracają więcej wykryć z możliwymi niemal-duplikatami.
x_stride i y_stride określają krok skanowania głosowania, tak samo jak w pozostałych detektorach. Domyślne wartości 2 i 1 są odpowiednie dla większości obrazów; podniesienie obu do 4 to standardowy kompromis na rzecz szybkości dla obrazu, o którym wiadomo, że zawiera duże okręgi.
Każdy zwrócony Circle niesie x, y, r (środek i promień) oraz magnitude (sumę głosów, przydatną jako ocena pewności do sortowania lub filtrowania). Narysowanie wykrycia z powrotem na ramce to jedno wywołanie – draw_circle() przyjmuje 3-elementową krotkę (x, y, r), dostępną bezpośrednio z wyniku jako (c.x, c.y, c.r).
5.27.2. Prostokąty¶
find_rects() zapożycza detektor czworokątów z potoku AprilTag – ta sama procedura, która lokalizuje czarny kwadrat wokół znacznika, jest udostępniona samodzielnie jako uniwersalny wyszukiwacz prostokątów.
rects = img.find_rects(threshold=12000)
for r in rects:
img.draw_rectangle(r.rect, color=(0, 255, 0))
for corner in r.corners:
img.draw_circle((corner[0], corner[1], 4),
color=(0, 255, 0))
threshold to minimalna suma wielkości krawędzi wzdłuż obwodu prostokąta. Wydrukowany czarny prostokąt na białym tle w dobrze oświetlonej ramce z łatwością przekracza 10000; słabo widoczny prostokąt na fakturowanym tle może wymagać spadku do 2000 – kosztem fałszywych wykryć na rzecz czułości. Podobnie jak w przypadku detektora okręgów, właściwa wartość wynika z szybkiego przebiegu dostrajającego z zamierzonymi celami w polu widzenia.
Detektor jest projekcyjny – znajduje czworokąty, których boki są proste, ale niekoniecznie równoległe lub wyrównane do osi. Etykieta oglądana pod kątem wygląda na obrazie jak trapez, a detektor prostokątów znajduje ją poprawnie; prostokąt wyrównany do osi jest tylko zdegenerowanym przypadkiem, w którym cztery rogi przypadkiem tworzą prostokątne pudełko. roi ogranicza obszar wyszukiwania; pozostałe argumenty słowne przyjmują wartości domyślne z potoku AprilTag i rzadko wymagają dostrajania.
Każdy zwrócony Rect niesie wyrównaną do osi ramkę ograniczającą – x, y, w, h, plus 4-elementową krotkę rect, której oczekuje draw_rectangle() – oraz cztery wykryte rogi jako corners. Ramka ograniczająca jest tym, czego aplikacja używa do przybliżonej pozycji i rozmiaru; rogi opisują sam czworokąt projekcyjny. Gdy kamera ogląda płaski cel pod kątem, a aplikacja musi cofnąć efekt trapezowy – odczytać tekst na etykiecie, pobrać próbkę koloru z płaskiego pola – rogi trafiają bezpośrednio do rotation_corr() ze słowem kluczowym corners= (zobacz korekcja obiektywu i perspektywy), a wynikiem jest skorygowany prostokąt gotowy do dalszej analizy.
Ostrzeżenie
Ponieważ detektor jest dostrojony do tego, czego potrzebuje potok AprilTag – czworokątów o mocnych, wysokokontrastowych obramowaniach, jak czarny obrys znacznika na białym papierze – nie jest to przebieg znajdujący każdy prostokąt. Prostokąty o słabym kontraście, fakturowanych krawędziach lub w zatłoczonym otoczeniu mogą pozostać całkowicie niewykryte. To, jak dobrze działa, zależy od sytuacji: przetestuj go wcześnie na rzeczywistych celach, zanim zbudujesz wokół niego cały potok.
5.27.3. Gdy detektor pudłuje¶
Szczególnie okręgi korzystają z wstępnego filtru na wejściu. Zaszumiona ramka daje mnóstwo rozproszonych pikseli krawędzi, które wszystkie głosują, a powstała przestrzeń Hougha ma szerokie, rozmyte szczyty, które łączącemu trudno rozdzielić. Przebieg gaussian() lub mean() przed find_circles() wygładza szum, pozostawiając prawdziwe krawędzie nienaruszone; detektor zwraca czystsze szczyty w krótszym czasie.
W przypadku prostokątów częstym trybem awarii jest sytuacja odwrotna: niski kontrast między prostokątem a jego tłem oznacza, że suma wielkości krawędzi nigdy nie przekracza threshold. Przebieg histeq(), który redystrybuuje zakres jasności na pełny rozrzut od 0 do 255, przywraca kontrast, którego potrzebuje detektor. (Kontrast musi istnieć gdzieś na obrazie; wyrównanie histogramu może jedynie wzmocnić to, co już tam jest.)