5.27. Hitta cirklar och rektanglar¶
Linjer och segment täcker de raka kanterna i den fångade bildrutan, men många särdrag i verkligheten som kameran letar efter är inte raka. Ett mynt som ligger på ett skrivbord är en cirkel. En tryckt etikett, en klisterlapp, ovansidan av en låda sedd från sidan är en fyrhörning. Bildmodulen tillhandahåller en särskild detektor för var och en: en Hough-baserad sökning efter cirklar och en AprilTag-härledd sökning efter fyrsidiga former.
Båda metoderna följer samma mall som linjedetektorerna gör – threshold styr hur många röster en detektering behöver, roi snävar in sökningen, och de returnerade objekten bär både en position och en konfidensmagnitud – men parameterutrymmena och de rätta standardvärdena skiljer sig tillräckligt mycket för att förtjäna en uttrycklig genomgång.
5.27.1. Hough-cirklar¶
find_circles() kör den cirkulära varianten av Hough-transformen. Varje kantpixel från Sobel-förbehandlingen röstar på varje cirkel som skulle kunna passera genom den; cirklar som samlar tillräckligt med röster returneras.
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 är den minsta summan av Sobel-kantmagnituder längs kandidatcirkeln. Större cirklar spårar fler pixlar och behöver därför högre tröskelvärden för att godkännas; ett värde som hittar ett mynt med 20 pixlars radie kommer också att utlösas av brus runt en kant på 100 pixlar, medan ett värde som är inställt för det stora myntet kommer att missa det lilla. När det önskade radieintervallet är känt skalar det rätta tröskelvärdet med omkretsen – ungefär threshold = 50 * 2 * pi * r ger en rimlig startpunkt och det rätta värdet följer av en kort inställningsomgång.
r_min, r_max och r_step ställer in radiesökningen. Utan gränser skulle detektorn söka varje radie från några få pixlar upp till bildens halva bredd, vilket både är långsamt och ett recept för falska positiva. Att sätta r_min och r_max så att de omsluter den förväntade målstorleken med en generös marginal (t.ex. r_min=15, r_max=25 för ett mynt som är känt för att vara runt 20 pixlar) minskar arbetet avsevärt och förbättrar rösternas signal-brusförhållande. r_step styr sökningens upplösning; större steg går snabbare och kan missa en cirkel vars verkliga radie hamnar mellan två samplade värden. Standardvärdet r_step=2 är en rimlig kompromiss.
x_margin, y_margin och r_margin styr sammanslagning av närliggande detekteringar, på samma sätt som theta_margin och rho_margin gör för linjedetektering. En enda fysisk cirkel i bilden röstar på ett kluster av kandidatcirklar vars centrum och radier överensstämmer inom några få pixlar; marginalerna kollapsar varje kluster till sin topp innan resultatlistan byggs. Större marginaler returnerar färre, mer säkra detekteringar; mindre marginaler returnerar fler detekteringar med möjliga näst intill dubbletter.
x_stride och y_stride styr stegen i röstningsskanningen, på samma sätt som de gör i de andra detektorerna. Standardvärdet 2 och 1 fungerar bra för de flesta bilder; att skruva upp båda till 4 är den vanliga hastighetsavvägningen för en bild som är känd för att innehålla stora cirklar.
Varje returnerad Circle bär x, y, r (centrum och radie) och magnitude (rösttotalen, användbar som konfidenspoäng för sortering eller filtrering). Att rita tillbaka detekteringen i bildrutan är ett enda anrop – draw_circle() tar (x, y, r)-trippeln, tillgänglig som (c.x, c.y, c.r) direkt från resultatet.
5.27.2. Rektanglar¶
find_rects() lånar fyrhörningsdetektorn från AprilTag-flödet – samma rutin som lokaliserar den svarta fyrkanten runt en tagg tillhandahålls separat som en allmän rektangelsökare.
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 är den minsta summan av kantmagnituder runt rektangelns omkrets. En tryckt svart-på-vit rektangel i en väl belyst bildruta klarar enkelt 10000; en svag rektangel mot en strukturerad bakgrund kan behöva sänkas till 2000 – en avvägning mellan falska positiva och känslighet. Liksom med cirkeldetektorn följer det rätta värdet av en snabb inställningsomgång med de avsedda målen i bild.
Detektorn är projektiv – den hittar fyrhörningar vars sidor är raka men inte nödvändigtvis parallella eller axeljusterade. En etikett sedd från sidan ser ut som en trapets i bilden, och rektangeldetektorn hittar den korrekt; en axeljusterad rektangel är bara specialfallet där de fyra hörnen råkar bilda en rätvinklig låda. roi begränsar sökningen; resten av nyckelordsargumenten tar sina standardvärden från AprilTag-flödet och behöver sällan ställas in.
Varje returnerad Rect bär den axeljusterade begränsningsrutan – x, y, w, h, plus rect-fyrtippeln som draw_rectangle() förväntar sig – och de fyra detekterade hörnen som corners. Begränsningsrutan är vad applikationen använder för ungefärlig position och storlek; hörnen beskriver själva den projektiva fyrhörningen. När kameran betraktar ett platt mål från en vinkel och applikationen behöver ångra perspektivförvrängningen – läsa text på en etikett, sampla färg från en plan yta – matas hörnen direkt in i rotation_corr() med nyckelordet corners= (se lins- och perspektivkorrigering), och resultatet är den uträtade rektangeln redo för vilken analys som än kommer härnäst.
Varning
Eftersom detektorn är inställd för vad AprilTag-flödet behöver – fyrhörningar med starka kanter med hög kontrast, som en svart taggkontur på vitt papper – är den inte en sökning som hittar varje rektangel. Rektanglar med mjuk kontrast, strukturerade kanter eller röriga omgivningar kan förbli helt oupptäckta. Hur väl den fungerar beror på situationen: testa den mot de verkliga målen tidigt, innan du bygger ett flöde runt den.
5.27.3. När detektorn slår fel¶
Cirklar i synnerhet gynnas av ett förfilter på indata. En brusig bildruta ger massor av kringströdda kantpixlar som alla röstar, och det resulterande Hough-utrymmet har breda gröttiga toppar som sammanslagaren har svårt att separera. En gaussian()- eller mean()-omgång före find_circles() jämnar bort bruset och lämnar de verkliga kanterna intakta; detektorn returnerar renare toppar på kortare tid.
För rektanglar är det vanliga felläget det motsatta: låg kontrast mellan rektangeln och dess bakgrund innebär att summan av kantmagnituder aldrig klarar threshold. En histeq()-omgång som fördelar om ljusstyrkeintervallet över hela spannet från 0 till 255 återställer den kontrast detektorn behöver. (Kontrasten måste finnas någonstans i bilden; histogramutjämning kan bara förstärka det som redan finns där.)