5.26. Vonalak és szakaszok keresése¶
Némely jeleneti jellemző nem összefüggő színes régió, hanem irányított egyenes él: a padlóra festett vonal, a két felület közötti varrat, egy nyomtatott téglalap oldala, egy ajtónyílás éle. Ha a foltdetektort kérjük meg ezek megtalálására, az rossz kérdésfeltevés – az él egy képpont széles, a folt algoritmus pedig területet-színnel keres, így a válasz üresen vagy zajosan érkezik vissza.
Az irányított élekhez a megfelelő detektor a Hough-vonaltranszformáció. Az image modul két változatban kínálja: a find_lines() végtelen vonalakat ad vissza (minden vonal a teljes képen átnyúlik); a find_line_segments() véges szakaszokat ad vissza (minden vonalnak a képkockán belül vannak végpontjai). Hogy az alkalmazásnak melyikre van szüksége, attól függ, hogy az érdekes élek a teljes képkockán átfolytonosak-e, vagy csak egy részét fedik le.
5.26.1. Hogyan működik a Hough-transzformáció¶
Mindkét detektor ugyanazon az alapötleten osztozik, ezért érdemes egyszer megérteni. Az image modul először egy Sobel-stílusú élszűrőt futtat a bemeneten, hogy minden képpontot pontozzon aszerint, mennyire valószínű, hogy egy irányított élen fekszik. Minden ilyen élképpont aztán szavaz az összes vonalra, amelyen feküdhet. A legtöbb szavazatot összegyűjtő vonalak nyernek.
Egy vonal a Hough-térben két számmal van paraméterezve: a theta, a vonal szöge (0 – 179 fok), és a rho, a merőleges távolság a kép origójától a vonalig (előjeles, képpontban). Minden vonal, amelyet a kép tartalmaz, egy pont a (theta, rho) térben. A bemenet minden élképpontja egy szavazattal járul hozzá minden olyan (theta, rho) kombinációhoz, amely összhangban van a pozíciójával – fogalmilag egy görbe a Hough-téren át. Ahol sok ilyen görbe keresztezi egymást, ott sok élképpont egyetért ugyanabban a vonalban, és ez a kereszteződés egy észlelés.
A detektor a Hough-tér azon lokális maximumait adja vissza, amelyek szavazatösszege meghalad egy küszöbértéket. Minden visszaadott Line mindkét reprezentációt hordozza: x1, y1, x2, y2 a végpontos formához (a kép határaira levágva a végtelen eset esetén), theta, rho a Hough-formához, valamint length és magnitude a mérethez, illetve a szavazatszámhoz.
5.26.2. Végtelen vonalak¶
A find_lines() lefuttatja a Hough-transzformációt, és visszaadja a legerősebb vonalakat, mindegyiket a teljes képen átnyújtva:
lines = img.find_lines(threshold=1500, theta_margin=25, rho_margin=25)
for l in lines:
img.draw_line(l, color=(255, 0, 0))
A threshold a minimális szavazatösszeg ahhoz, hogy egy vonal elfogadásra kerüljön. A szavazatösszeg minden hozzájáruló képpont Sobel-élnagyságait adja össze, így a nagyobb threshold értékek hosszabb vagy erősebb éleket követelnek az átjutáshoz – ami a megfelelő értéket egyaránt függővé teszi a kép felbontásától (egy hosszabb vonal nagyobb felbontásnál több szavazatot halmoz fel) és a jelenettől, így az adott alkalmazáshoz kell hangolni. Durva kiindulópontként a hangoláshoz: 1000 egy szerény vonalhoz tiszta képen, 500 vagy az alatt gyenge kontraszt vagy rövid vonalak esetén, 2000 vagy több zsúfolt jelenetekhez, ahol téves-pozitív vonalak keletkeznek élzajfürtökön keresztül.
A theta_margin és rho_margin a közeli maximumok összevonását szabályozza. Egyetlen fizikai él egy kis fürt magas szavazatú rekeszt hoz létre a valódi (theta, rho) körül, és a detektor minden fürtöt a csúcsára zsugorít visszaadás előtt. A theta_margin=25 (fok) összevon minden olyan csúcsot, amely 25 fokon belül van az irányítás tekintetében; a rho_margin=25 (képpont) összevonja a 25 képponton belüli távolságú csúcsokat. Az alapértékek ésszerűek; emelésük kevesebb, jobban elkülönülő vonalat ad vissza, csökkentésük pedig több, néha duplikált vonalat ad vissza.
Az x_stride és y_stride lépteti az élképpontokat a szavazás során, ugyanúgy, ahogy a find_blobs() lépteti a képpontokat. Az alapértelmezett 2 és 1 a gyakori esetre működik; emelésük felgyorsítja a keresést a felbontás rovására. A roi a keresést a képkocka egy régiójára korlátozza, ami egyszerre szűkíti a visszaadott vonalakat és csökkenti a munkát.
Minden visszaadott vonal közvetlenül rajzolható: a Line objektum egyenesen átadható a draw_line() metódusnak, amely az elejéről kiolvassa az (x1, y1, x2, y2) végpontmezőket. Az l.theta a szög fokban, amely egyetlen összehasonlítással vízszintesként, függőlegesként vagy átlósként osztályozza a vonalat. Az l.magnitude a szavazatösszeg, amely a visszaadott vonalakat a legerősebbtől a leggyengébbig rendezi.
5.26.3. Vonalszakaszok¶
A find_lines() a megfelelő detektor a teljes képkockát átívelő élekhez, de sok valós él – egy nyomtatott vonalkód bal oldala, egy címke felső éle, egy vonalzó látható oldala – csak a kép egy részén fut át. A find_line_segments() véges szakaszokat ad vissza, amelyek végpontjai a képkockán belül vannak:
segments = img.find_line_segments(merge_distance=5, max_theta_difference=10)
for s in segments:
img.draw_line(s, color=(0, 255, 0))
A szakaszdetektor közvetlenül az irányított élképpontok mentén követ, ahelyett, hogy a Hough-térben szavazna, és az eredmény rövid egyenes futamok gyűjteménye. A merge_distance állítja be a maximális képponttávolságot, amelyet két egyvonalban lévő rövid futam áthidalhat, és mégis egyetlen visszaadott szakaszba olvad össze; a max_theta_difference állítja be, hogy hány fok irányultságkülönbséget tűr el az összevonó a szomszédos futamok között. A nagyvonalú összevonás (merge_distance=10, max_theta_difference=15) kis számú hosszú szakaszt ad vissza, néha valóban különálló élek áthidalásának árán; a szigorú összevonás (merge_distance=0, max_theta_difference=5) sok rövid szakaszt ad vissza, és az alkalmazásra bízza, hogy Pythonban válogassa szét őket.
Az eredményobjektumok ugyanaz a Line típus, mint amit a find_lines() ad vissza, ugyanazokkal a tulajdonságokkal, így egy folyamat mindkét fajta észlelést ugyanazon a továbbfeldolgozási kódútvonalon dolgozhatja fel. Az egyetlen gyakorlati különbség az, hogy a szakaszok végpontjai a vonal tényleges végei a képen, míg a végtelen vonalak végpontjai ott vannak, ahol a vonal átlépi a kép határát.
5.26.4. Mikor melyiket használjuk¶
A két módszer közötti választás egyetlen kérdésre vezethető vissza: érdekli-e az alkalmazást, hol áll meg a vonal?
A find_lines() a megfelelő eszköz, amikor a válasz nem. Egy vonalkövető robotnak tudnia kell, merre tart a vonal és hol keresztezi a képkocka alját; maga a vonal a horizontig és azon túl fut. Egy horizontdetektor a kép legerősebb irányított élét akarja; nem kell tudnia, hol ér véget a horizont.
A find_line_segments() a megfelelő eszköz, amikor a válasz igen. Egy nyomtatott téglalap négy oldalának azonosításához négy szakaszra van szükség ismert végpontokkal. Egy kijelzőre mutató ujj követése egy rövid szakasz követését jelenti, amelynek végpontjai az ujj hegye és töve. Egy látható karcolás hosszának méréséhez a szakasz tényleges kiterjedésére van szükség képpontban.
Mindkét detektor egy közös korláton osztozik: kontrasztra van szükségük. A Sobel-élszűrő, amelyre épülnek, a fényerő-gradiensekre reagál; egy színes él egy ugyanolyan világos háttér előtt (egy piros vonal egy azonos fényességű zöld falon) nem hoz létre gradienst és nem ad észlelést. Amikor ez az eset a gyakorlatban felmerül, a megoldás egyetlen LAB-csatorna szürkeárnyalatos képként való kinyerése a megfelelő kontraszttal a keresés előtt – a to_grayscale() a b csatornával kiválasztva elkülöníti a pirosat a zölddel szemben ott, ahol a fényerő-csatorna önmagában lapos –, majd ennek a csatornaképnek az átadása a vonaldetektornak.