5.27. Cirkels en rechthoeken vinden

Lijnen en segmenten beslaan de rechte randen in het vastgelegde frame, maar veel kenmerken in de echte wereld waar de camera naar zoekt zijn niet recht. Een munt die op een bureau ligt is een cirkel. Een gedrukt label, een plakbriefje, de bovenkant van een doos die schuin bekeken wordt, is een vierhoek. De image-module biedt voor elk een speciale detector: een Hough-achtige zoekopdracht voor cirkels en een van AprilTag afgeleide zoekopdracht voor vierzijdige vormen.

Beide methoden volgen hetzelfde sjabloon als de lijndetectors – threshold bepaalt hoeveel stemmen een detectie nodig heeft, roi versmalt de zoekopdracht en de teruggegeven objecten dragen zowel een positie als een betrouwbaarheidsgrootte – maar de parameterruimtes en de juiste standaardwaarden verschillen genoeg om expliciete behandeling te verdienen.

5.27.1. Hough-cirkels

find_circles() voert de cirkelvariant van de Hough-transformatie uit. Elke randpixel uit de Sobel-voorbewerking stemt op elke cirkel die er doorheen zou kunnen lopen; cirkels die genoeg stemmen verzamelen worden teruggegeven.

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 is de minimale som van Sobel-randgroottes langs de kandidaatcirkel. Grotere cirkels doorlopen meer pixels en hebben dus hogere drempelwaarden nodig om door te komen; een waarde die een munt met een straal van 20 pixels vindt, zal ook afgaan op ruis rond een rand van 100 pixels, terwijl een waarde die is afgestemd op de grote munt de kleine zal missen. Wanneer het bereik van de doelstraal bekend is, schaalt de juiste drempelwaarde mee met de omtrek – ongeveer threshold = 50 * 2 * pi * r geeft een redelijk startpunt en de juiste waarde volgt uit een korte afstemronde.

r_min, r_max en r_step stellen de straalzoekopdracht in. Zonder grenzen zou de detector elke straal doorzoeken van een paar pixels tot de halve breedte van de afbeelding, wat zowel traag is als een recept voor valse positieven. r_min en r_max zo instellen dat ze de verwachte doelgrootte met een ruime marge omsluiten (bijv. r_min=15, r_max=25 voor een munt waarvan bekend is dat hij rond de 20 pixels is) vermindert het werk aanzienlijk en verbetert de signaal-ruisverhouding van de stemmen. r_step bepaalt de granulariteit van de zoekopdracht; grotere stappen lopen sneller en kunnen een cirkel missen waarvan de werkelijke straal tussen twee bemonsterde waarden valt. De standaardwaarde r_step=2 is een redelijk compromis.

x_margin, y_margin en r_margin bepalen het samenvoegen van nabijgelegen detecties, op dezelfde manier als theta_margin en rho_margin dat doen bij lijndetectie. Eén fysieke cirkel in de afbeelding stemt op een cluster van kandidaatcirkels waarvan de middelpunten en stralen tot op enkele pixels overeenkomen; de marges reduceren elk cluster tot zijn piek voordat de resultatenlijst wordt opgebouwd. Grotere marges geven minder, betrouwbaardere detecties terug; kleinere marges geven meer detecties terug met mogelijke bijna-duplicaten.

x_stride en y_stride bepalen de stapgrootte van de stemscan, op dezelfde manier als bij de andere detectors. De standaardwaarden van 2 en 1 zijn prima voor de meeste afbeeldingen; beide opvoeren naar 4 is de standaard snelheidsafweging voor een afbeelding waarvan bekend is dat hij grote cirkels bevat.

Elke teruggegeven Circle draagt x, y, r (het middelpunt en de straal) en magnitude (het totaal aantal stemmen, nuttig als betrouwbaarheidsscore voor sorteren of filteren). De detectie terugtekenen in het frame is één aanroep – draw_circle() neemt de 3-tuple (x, y, r) aan, rechtstreeks beschikbaar als (c.x, c.y, c.r) uit het resultaat.

5.27.2. Rechthoeken

find_rects() leent de vierhoekdetector van de AprilTag-pijplijn – dezelfde routine die het zwarte vierkant rond een tag lokaliseert, wordt op zichzelf beschikbaar gesteld als een algemene rechthoekzoeker.

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 is de minimale som van randgroottes rond de omtrek van de rechthoek. Een gedrukte zwart-op-witte rechthoek in een goed verlicht frame overschrijdt gemakkelijk 10000; een vage rechthoek op een getextureerde achtergrond moet mogelijk dalen tot 2000 – waarbij valse positieven worden afgewogen tegen gevoeligheid. Net als bij de cirkeldetector volgt de juiste waarde uit een snelle afstemronde met de beoogde doelen in beeld.

De detector is projectief – hij vindt vierhoeken waarvan de zijden recht zijn maar niet noodzakelijk parallel of asgericht. Een label dat schuin bekeken wordt ziet eruit als een trapezium in de afbeelding, en de rechthoekdetector vindt het correct; een asgerichte rechthoek is slechts het ontaarde geval waarin de vier hoeken toevallig een rechthoekige doos vormen. roi beperkt de zoekopdracht; de rest van de trefwoordargumenten nemen hun standaardwaarden over uit de AprilTag-pijplijn en hoeven zelden te worden afgestemd.

Elke teruggegeven Rect draagt het asgerichte begrenzingsvak – x, y, w, h, plus de 4-tuple rect die draw_rectangle() verwacht – en de vier gedetecteerde hoeken als corners. Het begrenzingsvak is wat de applicatie gebruikt voor ruwe positie en grootte; de hoeken beschrijven de projectieve vierhoek zelf. Wanneer de camera een plat doel onder een hoek bekijkt en de applicatie de keystone ongedaan moet maken – tekst op een label lezen, kleur uit een plat vlak bemonsteren – worden de hoeken rechtstreeks aan rotation_corr() doorgegeven met het trefwoord corners= (zie lens- en perspectiefcorrectie), en de uitvoer is de rechtgezette rechthoek, klaar voor welke analyse er ook volgt.

Waarschuwing

Omdat de detector is afgestemd op wat de AprilTag-pijplijn nodig heeft – vierhoeken met sterke, contrastrijke randen, zoals een zwarte tagomtrek op wit papier – is het geen vind-elke-rechthoek-doorloop. Rechthoeken met zacht contrast, getextureerde randen of drukke omgevingen kunnen volledig onopgemerkt blijven. Hoe goed het werkt is situatieafhankelijk: test het vroeg tegen de echte doelen, voordat je er een pijplijn omheen bouwt.

5.27.3. Wanneer de detector misvuurt

Vooral cirkels profiteren van een voorfilter op de invoer. Een ruisrijk frame levert veel verspreide randpixels op die allemaal stemmen, en de resulterende Hough-ruimte heeft brede, vage pieken die de samenvoeger moeilijk kan scheiden. Een gaussian()- of mean()-doorloop vóór find_circles() strijkt de ruis weg en laat de echte randen intact; de detector geeft schonere pieken terug in minder tijd.

Voor rechthoeken is de gebruikelijke faalmodus het tegenovergestelde: laag contrast tussen de rechthoek en zijn achtergrond betekent dat de som van de randgroottes nooit threshold overschrijdt. Een histeq()-doorloop om het helderheidsbereik over het volledige bereik van 0 tot 255 te herverdelen, herstelt het contrast dat de detector nodig heeft. (Het contrast moet ergens in de afbeelding aanwezig zijn; histogramegalisatie kan alleen versterken wat er al is.)