5.27. Detectarea cercurilor și a dreptunghiurilor

Liniile și segmentele acoperă muchiile drepte din cadrul capturat, dar multe dintre caracteristicile din lumea reală pe care le caută camera nu sunt drepte. O monedă așezată pe un birou este un cerc. O etichetă tipărită, un bilețel adeziv, partea de sus a unei cutii privită din unghi sunt un patrulater. Modulul image expune câte un detector dedicat pentru fiecare: o căutare de tip Hough pentru cercuri și o căutare derivată din AprilTag pentru formele cu patru laturi.

Ambele metode urmează același șablon ca detectoarele de linii – threshold controlează câte voturi necesită o detectare, roi restrânge căutarea, iar obiectele returnate poartă atât o poziție, cât și o magnitudine de încredere – însă spațiile parametrilor și valorile implicite potrivite diferă suficient pentru a merita o tratare explicită.

5.27.1. Cercuri Hough

find_circles() rulează varianta circulară a transformatei Hough. Fiecare pixel de muchie din etapa preliminară Sobel votează pentru fiecare cerc care ar putea trece prin el; cercurile care adună suficiente voturi sunt returnate.

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 este suma minimă a magnitudinilor muchiilor Sobel de-a lungul cercului candidat. Cercurile mai mari trasează mai mulți pixeli și au nevoie, prin urmare, de praguri mai mari pentru a trece; o valoare care găsește o monedă cu rază de 20 de pixeli se va declanșa și pe zgomotul din jurul unei muchii de 100 de pixeli, în timp ce o valoare reglată pentru moneda mare o va rata pe cea mică. Atunci când intervalul de raze al țintei este cunoscut, pragul potrivit se scalează cu circumferința – aproximativ threshold = 50 * 2 * pi * r oferă un punct de plecare rezonabil, iar valoarea corectă rezultă dintr-o scurtă etapă de reglaj.

r_min, r_max și r_step stabilesc căutarea razei. Fără limite, detectorul ar căuta fiecare rază, de la câțiva pixeli până la jumătatea lățimii imaginii, ceea ce este atât lent, cât și o rețetă pentru rezultate false. Stabilirea valorilor r_min și r_max astfel încât să încadreze dimensiunea așteptată a țintei cu o marjă generoasă (de exemplu r_min=15, r_max=25 pentru o monedă despre care se știe că are în jur de 20 de pixeli) reduce substanțial volumul de lucru și îmbunătățește raportul semnal-zgomot al voturilor. r_step controlează granularitatea căutării; pași mai mari rulează mai rapid și pot rata un cerc a cărui rază reală se află între două valori eșantionate. Valoarea implicită r_step=2 este un compromis rezonabil.

x_margin, y_margin și r_margin controlează fuzionarea detectărilor apropiate, la fel cum o fac theta_margin și rho_margin pentru detectarea liniilor. Un singur cerc fizic din imagine votează pentru un grup de cercuri candidate ale căror centre și raze coincid cu o precizie de câțiva pixeli; marjele restrâng fiecare grup la vârful său înainte de construirea listei de rezultate. Marjele mai mari returnează mai puține detectări, dar mai sigure; marjele mai mici returnează mai multe detectări, cu posibile cvasi-duplicate.

x_stride și y_stride stabilesc pasul scanării de votare, la fel ca în celelalte detectoare. Valorile implicite de 2 și 1 sunt potrivite pentru majoritatea imaginilor; creșterea ambelor la 4 este compromisul standard de viteză pentru o imagine despre care se știe că conține cercuri mari.

Fiecare obiect Circle returnat poartă x, y, r (centrul și raza) și magnitude (totalul voturilor, util ca scor de încredere pentru sortare sau filtrare). Desenarea detectării înapoi în cadru se face printr-un singur apel – draw_circle() primește tripletul (x, y, r), disponibil direct din rezultat ca (c.x, c.y, c.r).

5.27.2. Dreptunghiuri

find_rects() împrumută detectorul de patrulatere din procesul AprilTag – aceeași rutină care localizează pătratul negru din jurul unei etichete este expusă separat ca un detector de dreptunghiuri de uz general.

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 este suma minimă a magnitudinilor muchiilor de pe perimetrul dreptunghiului. Un dreptunghi tipărit cu negru pe alb într-un cadru bine iluminat depășește cu ușurință 10000; un dreptunghi slab vizibil pe un fundal texturat poate necesita coborârea până la 2000 – sacrificând rezultate false pentru sensibilitate. Ca și la detectorul de cercuri, valoarea potrivită rezultă dintr-o scurtă etapă de reglaj, cu țintele dorite în câmpul vizual.

Detectorul este proiectiv – găsește patrulatere ale căror laturi sunt drepte, dar nu neapărat paralele sau aliniate cu axele. O etichetă privită din unghi arată ca un trapez în imagine, iar detectorul de dreptunghiuri o găsește corect; un dreptunghi aliniat cu axele este doar cazul degenerat în care cele patru colțuri formează întâmplător o casetă cu unghiuri drepte. roi restrânge căutarea; restul argumentelor cu cuvinte-cheie își iau valorile implicite din procesul AprilTag și rareori necesită reglaje.

Fiecare obiect Rect returnat poartă caseta de încadrare aliniată cu axele – x, y, w, h, plus cvadruplul rect pe care îl așteaptă draw_rectangle()și cele patru colțuri detectate sub forma corners. Caseta de încadrare este ceea ce folosește aplicația pentru poziția și dimensiunea aproximativă; colțurile descriu patrulaterul proiectiv în sine. Atunci când camera privește o țintă plană dintr-un unghi și aplicația trebuie să anuleze efectul de trapez – pentru a citi text de pe o etichetă, a eșantiona culoarea de pe o suprafață plană – colțurile sunt transmise direct către rotation_corr() prin cuvântul-cheie corners= (vezi corecția lentilei și a perspectivei), iar rezultatul este dreptunghiul rectificat, gata pentru orice analiză urmează în continuare.

Atenționare

Deoarece detectorul este reglat pentru ceea ce are nevoie procesul AprilTag – patrulatere cu margini puternice, cu contrast ridicat, precum conturul negru al unei etichete pe hârtie albă – el nu este o trecere care găsește fiecare dreptunghi. Dreptunghiurile cu contrast slab, muchii texturate sau cu un fundal aglomerat pot rămâne complet nedetectate. Cât de bine funcționează depinde de situație: testează-l devreme pe țintele reale, înainte de a construi un proces în jurul lui.

5.27.3. Când detectorul dă greș

Cercurile în special beneficiază de o prefiltrare a intrării. Un cadru zgomotos produce mulți pixeli de muchie răzleți care votează cu toții, iar spațiul Hough rezultat are vârfuri largi și estompate pe care procesul de fuzionare le separă cu greu. O trecere gaussian() sau mean() înainte de find_circles() netezește zgomotul și lasă intacte muchiile reale; detectorul returnează vârfuri mai curate în mai puțin timp.

Pentru dreptunghiuri, modul obișnuit de eșec este invers: contrastul scăzut dintre dreptunghi și fundalul său înseamnă că suma magnitudinilor muchiilor nu depășește niciodată threshold. O trecere histeq() care redistribuie intervalul de luminozitate pe întreaga plajă de la 0 la 255 restabilește contrastul de care are nevoie detectorul. (Contrastul trebuie să existe undeva în imagine; egalizarea histogramei poate doar să amplifice ceea ce este deja prezent.)