5.27. Hledání kružnic a obdélníků

Čáry a úsečky pokrývají přímé hrany v zachyceném snímku, ale spousta reálných příznaků, které kamera hledá, přímá není. Mince ležící na stole je kružnice. Vytištěný štítek, lepicí lísteček nebo horní strana krabice viděná mimo osu je čtyřúhelník. Modul image nabízí pro každý z nich vyhrazený detektor: vyhledávání kružnic Houghova typu a vyhledávání čtyřstranných tvarů odvozené z AprilTag.

Obě metody se řídí stejnou šablonou jako detektory čar – threshold určuje, kolik hlasů detekce potřebuje, roi zužuje oblast hledání a vrácené objekty nesou jak pozici, tak velikost spolehlivosti – ale prostory parametrů a vhodné výchozí hodnoty se liší natolik, že si zaslouží samostatný popis.

5.27.1. Houghovy kružnice

find_circles() spouští kruhovou variantu Houghovy transformace. Každý hranový pixel z předchozího Sobelova průchodu hlasuje pro každou kružnici, která by jím mohla procházet; vrácené jsou kružnice, které nasbírají dostatek hlasů.

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 je minimální součet velikostí Sobelových hran podél kandidátní kružnice. Větší kružnice obkreslují více pixelů, a proto potřebují vyšší prahy, aby prošly; hodnota, která najde minci o poloměru 20 pixelů, se spustí i na šumu kolem hrany o velikosti 100 pixelů, zatímco hodnota vyladěná pro velkou minci tu malou mine. Když je rozsah cílových poloměrů znám, vhodný práh roste s obvodem – zhruba threshold = 50 * 2 * pi * r dává rozumný výchozí bod a správná hodnota se získá krátkým laděním.

r_min, r_max a r_step nastavují vyhledávání poloměru. Bez mezí by detektor prohledával každý poloměr od několika pixelů až po polovinu šířky obrazu, což je jednak pomalé, jednak je to recept na falešné pozitivy. Nastavení r_min a r_max tak, aby s velkorysou rezervou ohraničovaly očekávanou velikost cíle (např. r_min=15, r_max=25 pro minci, o které víme, že má kolem 20 pixelů), výrazně sníží práci a zlepší poměr signálu k šumu u hlasů. r_step určuje jemnost hledání; větší kroky běží rychleji a mohou minout kružnici, jejíž skutečný poloměr leží mezi dvěma vzorkovanými hodnotami. Výchozí r_step=2 je rozumný kompromis.

x_margin, y_margin a r_margin řídí slučování blízkých detekcí podobně, jako to dělají theta_margin a rho_margin při detekci čar. Jediná fyzická kružnice v obraze hlasuje pro shluk kandidátních kružnic, jejichž středy a poloměry se shodují na pár pixelů přesně; rezervy sloučí každý shluk na jeho vrchol ještě před sestavením seznamu výsledků. Větší rezervy vracejí méně, ale spolehlivějších detekcí; menší rezervy vracejí více detekcí s možnými téměř duplikáty.

x_stride a y_stride posouvají skenování hlasování stejně jako u ostatních detektorů. Výchozí hodnoty 2 a 1 jsou pro většinu obrazů v pořádku; zvýšení obou na 4 je standardní kompromis ve prospěch rychlosti u obrazu, o kterém víme, že obsahuje velké kružnice.

Každá vrácená Circle nese x, y, r (střed a poloměr) a magnitude (celkový počet hlasů, užitečný jako skóre spolehlivosti pro řazení nebo filtrování). Vykreslení detekce zpět do snímku je jediné volání – draw_circle() přijímá trojici (x, y, r), dostupnou přímo z výsledku jako (c.x, c.y, c.r).

5.27.2. Obdélníky

find_rects() si vypůjčuje detektor čtyřúhelníků z pipeline AprilTag – tatáž rutina, která lokalizuje černý čtverec kolem tagu, je samostatně vystavena jako univerzální hledač obdélníků.

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 je minimální součet velikostí hran kolem obvodu obdélníku. Vytištěný černobílý obdélník v dobře osvětleném snímku snadno překoná 10000; slabý obdélník na texturovaném pozadí může vyžadovat snížení na 2000 – výměnou za falešné pozitivy získáte citlivost. Stejně jako u detektoru kružnic se vhodná hodnota získá rychlým laděním se zamýšlenými cíli v záběru.

Detektor je projektivní – nachází čtyřúhelníky, jejichž strany jsou přímé, ale ne nutně rovnoběžné nebo zarovnané s osami. Štítek viděný mimo osu vypadá v obraze jako lichoběžník a detektor obdélníků jej správně najde; obdélník zarovnaný s osami je jen degenerovaný případ, kdy čtyři rohy náhodou tvoří pravoúhlý rámeček. roi omezuje oblast hledání; zbytek klíčových argumentů přebírá výchozí hodnoty z pipeline AprilTag a jen zřídka potřebuje ladění.

Každý vrácený Rect nese ohraničující rámeček zarovnaný s osami – x, y, w, h plus čtveřici rect, kterou očekává draw_rectangle()a čtyři detekované rohy jako corners. Ohraničující rámeček je to, co aplikace používá pro hrubou pozici a velikost; rohy popisují samotný projektivní čtyřúhelník. Když kamera snímá plochý cíl pod úhlem a aplikace potřebuje odstranit zkreslení perspektivy – přečíst text na štítku, navzorkovat barvu z ploché plošky – rohy putují přímo do rotation_corr() s klíčovým slovem corners= (viz korekce objektivu a perspektivy) a výstupem je narovnaný obdélník připravený na jakoukoli další analýzu.

Varování

Protože je detektor vyladěn na to, co potřebuje pipeline AprilTag – čtyřúhelníky se silnými, vysoce kontrastními okraji, jako je obrys černého tagu na bílém papíře – nejde o průchod, který najde každý obdélník. Obdélníky s měkkým kontrastem, texturovanými hranami nebo rušným okolím mohou zůstat zcela nedetekovány. Jak dobře funguje, závisí na situaci: vyzkoušejte jej proti reálným cílům včas, dříve než kolem něj postavíte pipeline.

5.27.3. Když detektor selže

Zejména kružnice profitují z předfiltru na vstupu. Zašuměný snímek vytváří spoustu zbloudilých hranových pixelů, které všechny hlasují, a výsledný Houghův prostor má široké rozmazané vrcholy, které slučovač jen těžko odděluje. Průchod gaussian() nebo mean() před find_circles() šum vyhladí a skutečné hrany ponechá nedotčené; detektor vrátí čistší vrcholy za kratší dobu.

U obdélníků je běžným způsobem selhání opak: nízký kontrast mezi obdélníkem a jeho pozadím znamená, že součet velikostí hran nikdy nepřekoná threshold. Průchod histeq(), který přerozdělí rozsah jasu napříč celým rozpětím 0 až 255, obnoví kontrast, který detektor potřebuje. (Kontrast musí někde v obraze existovat; ekvalizace histogramu dokáže zesílit jen to, co tam už je.)