Skip navigation

Egyenesek és szakaszok kinyerése

Kapcsolódó bemutató videók

Az egyenes detekcióhoz kapcsolódóan két bemutató videóanyag érhető el.

  • KfGyak_11_01_a_Hough_line_visualize_rho_theta.mp4: Egyenes egyenletének reprezentációja r (rho) és Theta paraméterekkel, a paraméterek vizualizációja.
  • KfGyak_11_01_b_Hough_line_algo_visualize.mp4: Összegzőtömb előállítása az objektumpontokon átmenő összes egyenes vizsgálatával.

Egyenes egyenlete

2D síkon egy egyenes egyenletét többféle módon felírhatjuk. Megjegyezzük, hogy az ábrákon nem a klasszikus, hanem az OpenCV képi koordináta-rendszere szerinti tengely irányokat vesszük alapul.

A klasszikus, középiskolában tanult y = Ax + B egyenlet használata problémát okoz, mert a függőleges egyenesek leírására nem képes.

Az egyenes egyenlete felírható az egyenes origótól mért távolsága és iránya alapján is. Itt nem okoz gondot a függőleges irány kezelése, így ezt választjuk. Az r paramétert gyakran rho-ként hivatkozzák, így Theta-rho paraméterezésnek is nevezik.

Vegyük észre, hogy az r és Theta ismeretében ki tudjuk számítani az egyenes egy pontjának helyét (a rá merőleges, origóból húzott r hosszúságú szakasz talppontjáét), az egyenes iránya pedig az r hoszúságú szakaszra merőleges lesz.

Theta-rho paraméterezés értelmezése

A távolság-irány alapú paraméterezés jobb megértéséhez használjuk a 11_01_a_hough_line_visualize_rho_theta.py példaprogramot.

A program indítása után a felső csúszkák segítségével az r és a theta paramétereket állíthatjuk. A paraméterek a vörös színű egyenest definiálják. A kék színű szakasz a vörös origótól mért távolságát jelzi.

A programban az r 0-tól a képmátrix átlójának nagyságáig változhat. Elvileg a [-1 * átlóhosz, átlóhossz] tartományban lenne értelme ezt engedni, az OpenCV csúszkájának korlátai miatt választjuk most csak a nemnegatív értékeket, ami a szemléltetéshez elegendő is. A theta [0, 180] közötti egész érték lehet. Figyeljük meg, hogy a 0 és a 180 fokos értékek egymással megegyező egyenest adnak. Az r változtatásával az egyenes párhuzamos eltolását végezhetjük. A theta az egyenest forgatja egy origó körüli, r sugarú kör mentén.

Figyeljük meg, hogy bizonyos értéktartományok olyan egyeneseket definiálnak, amelyek a képi tartományon kívül esnek! Ez természetes.

Feladat

Próbáljunk olyan paraméterezéseket találni, amelyek a Sudoku tábla rácsaihoz illeszkednek!

Hough transzformáció egyenes detektáláshoz

A Hough transzformáción alapuló egyenes irány detekció fő jellemzői az alábbiak:

  • A detekcióhoz bináris képre van szükség, ahol az objektumpontok (fehér színűek) a detektálandó egyeneseket reprezentáló pontok. Jellemzően éldetektor (pl. Canny) eredményt használunk fel.
  • A transzformáció minden objektumpontra összegzi, hogy rajta milyen egyenesek mennek keresztül. Csak diszkrét számú egyenest vizsgál az elforgatási szög mintavételezése szerint. A gyakorlatban jellemzően 1-2 fokos léptéket használunk, így 180 vagy 90 darab egyenes kerül vizsgálatra. A vizsgált egyenesek rho-theta paraméterei kiszámításra kerülnek, és egy rho-theta szerint indexelt összegzőtömbben növelésre kerül az adott egyeneshez tartozó számláló érték. Ezek a számláló értékek 0-ról indulnak.
  • Vegyük észre, hogy az előző pontban leírt algoritmus tulajdonképpen megszámolja, hogy egy rho-theta paraméterű egyenesre hány darab bináris objektumpont illeszkedik a képen. Minél nagyobb egy-egy érték az összegőtömbben, annál több ilyen illeszkedő pont van az adott egyenesre.
  • Ha a bináris kép minden objektumpontjára minden lehetséges egyenest megvizsgáltunk, akkor az összegzőtömb lokális maximumértékeit kell keresnünk. Ezek a lokális maximum rho-theta értékek megadják a keresett egyenesek paramétereit.
  • Így azt is szabályozhatjuk, hogy mennyire erős (legalább egy minimális számú támogató ponttal rendelkező) egyeneseket szeretnénk detektálni. Ez a küszöbérték erősen képi tartalom függő! Valamint ereősség szerint sorbarendezhetjük a detektált egyeneseket.

Az algoritmus működését a 11_01_b_hough_line_algo_visualize.py példaprogram szemlélteti.

A program három ablakot nyit meg.

  • Az elsőben láthatjuk a betöltött Sudoku kép Canny éldetektorral készített bináris élkép eredményét. Ezen keresünk egyeneseket. A képen színes körök jelzik azokat a pontokat, amelyeket az egyenes vizsgálathoz használunk. Kezdetben a sárga színű pont jelölődik ki.
  • A második ablak a diszkretizált theta-rho paraméterek szerinti összegzőtömböt mutatja. A theta paraméter szerint 2 fokos, a rho szerint 4 képpontos léptékkel dolgozunk, és az eredményt a cellák jobb vizuális elkülöníthetősége miatt négyszeres nagyításban mutatjuk. Az összegző tömbben a világosabb értékek jelzik azokat az egyeneseket, amelyekhez több támogató pont tartozik az élképen.
  • A harmadik diagram az adott színű ponthoz vizsgált egyenes paraméterértékeit ábrázolja.

Figyeljük meg, hogy a második ablak a harmadik diszkretizált változatát mutatja, valamint a vízszintes középtengelyre tükrözve van. Ezutóbbi az OpenCV lefelé, míg a diagram felfelé mutató Y-tengely iránya miatt van. Mindkét ablakban egy-egy pont egy-egy képtérbeli egyenest reprezentál.

Első kísérlet:

  • A program indítása után az első ablakban csúszka segítségével állthatjuk a színes ponton áthaladó egyenes irányát. Ezt a vörös színű egyenes rögtön mutatja is.
  • Figyeljük meg, hogy ez a változás az r érték nemlináris változását vonja maga után! Az így lapott r és theta paraméterek szerinti egyenes pontja megjelenik a harmadik diagramon, valamint az összegzőtömb vonatkozó indexű eleme növelődik, árnyalata világosabb lesz.
  • Futtassuk végig a csúszkát a 0-180 tartományon!
  • Az összegzőtömbben mozgassuk az egeret a szürke színnel kirajzolódó szinuszoid pontjai közelébe. Az egér mozgatása során az alatta található paraméterek által reprezenetált egyenes zöld színnel jelenik meg a képtérben. A szinuszoid pontjaira mozogva a színes ponton áthaladó zöld egyeneseket látunk. Fel-le eltérve a képtérben párhuzamosan mozog a zöld egyenes, balra-jobbra irányba mozogva pedig az egyenes az origó körül elfordul.
  • Kattintsunk a bal egérgombbal egy másik pontra a bal oldali élképen! Kék színű pont jelenik meg. A csúszka mozgatásával ezen a ponton keresztülmenő egyenesek kapunk, és frissülnek az összegzőtömb és a diagram adatai is.
  • Vizsgáljuk meg, milyen egyenes tartozik a két szinuszoid görbe metszéspontjához?
  • Adjunk hozzá tovább pontokat, és mozgassuk a csúszkát! Próbáljuk előre kitalálni, mi fog változni a másik két ablakban! Vizsgáljuk meg a szinuszoidok metszéspontjainak környezetét!

Második kísérlet:

  • A program újraindítása után nyomjuk meg az 'a' billentyűt. Ez az éppen érvényesen kijelölt ponthoz tartozó, 2 fokos léptékkel előálló összes egyenest megvizsgálja, a paramétereik pedig bekerülnek az összegzőtömbbe, és a grafikonon is ábrázolódnak.
  • A bal egérgombbal kattintva vegyünk fel új pontokat, és az 'a' billenytűvel adjuk hozzá az összes vizsgált egyenes adatait!

Harmadik kísérlet:

  • A program újraindítása után nyomjuk meg az 'a' billentyűt.
  • Amint az első ponthoz tartozó egyenesek vizsgálata kész, nyomjuk meg a 'p' billenytűt. Ez további 6 pontra elvégzi az egyenesek vizsgálatát.
  • Mozgassuk a szálkeresztet az összegzőtömb ablakban és magyarázzuk meg, mit látunk és miért.

Figyeljük meg!

  • Szinuszoid görbék találkozása olyan esetekben is előfordul, ahol a képen nem fut egyenes, csak két pontot köt össze. Minél több szinuszoid találkozik egy pontban, annál több pont esik arra az egyenesre. Ezért kell elegendő számú objektumpont az élképen egy-egy egyeneshez.
  • Az algoritmus nem vizsgálja az egyenes vonalszakaszok folytonosságát sem. Két, egy egyenesre eső szakaszt is egy egyenessel fog detektálni, még ha azok távol is vannak egymástól. Ha végpontjaikkal megadott vonalszakaszokat szeretnénk eredményül kapni, akkor más algoritmust kell használni. (Az OpenCV probabilisztikus Hough transzformációja ilyen eredményt ad, de ezzel az algoritmussal részletesen nem foglalkozuk, csak a használatát ismertetjük később.)
  • Ha az élképen található összes fehér színű objektumpontra megcsináljuk ezt a vizsgálatot, akkor az összegzőtömb elemzése következik, ahol lokális optimumokat kell keresni.

Egyenes detektálása Hough transzformációval (OpenCV)

Angol nyelvű dokumentáció

Standard változat

Az egyenes vonalak leírása poláris koordináta-rendszerben: távolsága az origótól és függőleges tengellyel bezárt szöge.

lines = cv.HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]])

lines

Detektált vonalak paraméter vektora. Minden egyenest két érték határoz meg: (ρ, θ).
ρ: távolság az origótól (bal felső sarok).
θ: bezárt szög az Y-tengely irányával radiánban (0: függőleges; π/2: vízszintes).

A tapasztalat azt mutatja, hogy az egyenes paramétereket szavazatszám alapján sorba rendezve kapjuk meg.
Vagyis az első elem lesz az az egyenes, amely a legtöbb ponton keresztülhalad.

image

Bementi kép. A függvény működése közben a tartalma megváltozhat!

rho Távolság összegzőtömb felbontása pixelben.
theta Bezárt szög összegzőtömb felbontása radiánban.
threshold Csak azok a vonalak kerülnek detektálásra, amelyek legalább threshold számú szavazatot kaptak.
srn Többskálás módszer esetén a rho paraméter osztója.
stn Többskálás módszer esetén a theta paraméter osztója.
min_theta Minimális szög. Alapértéke 0.
max_theta Maximális szög. Alapértéke CV_PI.

Használati példa

Figyeljük meg, hogyan rajzolhatjuk a képre a detektált egyeneseket! A rho és theta paraméterek alapján meghatározásra kerül az egyenes egy pontja. Erről balra és jobbra olyan távolságra választunk pontokat, amelyek feltehetőleg a képmátrixon kívül esnek. Ezek összekötésével a kép szélei közötti egyeneseket kapunk.

src = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
dst = cv2.Canny(src, 50, 200, None, 3)
cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)

lines = cv2.HoughLines(dst, 1, np.pi / 180, 300, None, 0, 0)


# Diagonal is the longest line that can be drawn
sizemax = math.sqrt(cdst.shape[0] ** 2 + cdst.shape[1] ** 2)
if lines is not None:

print('Detektált egyenesek száma:', len(lines))
for i in range(0, len(lines)):

    rho = lines[i][0][0]
    theta = lines[i][0][1]
    a = math.cos(theta)
    b = math.sin(theta)
    x0 = a * rho
    y0 = b * rho
  # Computing line endpoints outside of image matrix
pt1 = (int(x0 + sizemax * (-b)), int(y0 + sizemax * a))
pt2 = (int(x0 - sizemax * (-b)), int(y0 - sizemax * a))
    cv2.line(cdst, pt1, pt2, (0, 0, 255), 3, cv2.LINE_AA)
cv2.imshow("Source", src)
cv2.imshow("Detected Lines (in red) - Standard Hough Line Transform", cdst)

Az 54 legerősebb detektált egyenest az alábbi animáció mutatja, erősségi csökkenő sorrendben megjelenítve. Figyeljük meg, hogy az egyenesre eső szakaszok tetszőlegesen nagy távolságra lehetnek egymástól, ezek elkülönítésére a módszer nem képes. Ha elkülönülő vonalszakaszokat szeretnénk detektálni, akkor használjuk a probabilisztikus változatot!

Mivel a szöveg körvonala is megjelenik objektumpontokként, a módszer azokra a pontokra is vizsgálja az egyenesek illeszkedését.

Probabilisztikus változat

Végpont koordinátáikkal megadott vonalszegmensek. Megadható a minimális vonalhossz, valamint a megengedett legnagyobb szakadási hossz.

lines = cv.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])

lines Detektált vonalszakaszok vektora.
4 elemű tömbök listája, amelyek a vonalszakaszok végpontjait adják.
image Bementi kép.
rho Távolság összegzőtömb felbontása pixelben.
theta Bezárt szög összegzőtömb felbontása radiánban.
threshold Csak azok a vonalak kerülnek detektálásra, amelyek legalább threshold számú szavazatot kaptak.
minLineLength Minimális szegmens hossz. A kisebbek eldobásra kerülnek.
maxLineGap

Maximális megengedett távolság két egy irányba eső vonalszakasz között.
Kisebb távolság esetén összevonásra kerülnek.
Nagyobb távolság esetén két különálló szegmenst kapunk (ha a minimális hosszt elérik).

Használati példa

src = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
dst = cv2.Canny(src, 50, 200, None, 3)
cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)

linesP = cv2.HoughLinesP(dst, 1, np.pi / 180, 50, None, 50, 10) if linesP is not None:
for i in range(0, len(linesP)):
l = linesP[i][0]
cv2.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 3, cv2.LINE_AA)
cv2.imshow("Source", src)
cv2.imshow("Detected Lines (in red) - Probabilistic Line Transform", cdstP)

Egyenesek detektálása OpenCV példaprogram

11_01_c_hough_lines.py

  • A kétféle módszer együttes bemutatása.
  • A detektált eredmények képre rajzolása.

Feladatok

  • Módosítsunk a függvények paraméterezésén!
  • Egészítsük ki a programot úgy, hogy a detektálás paramétereit csúszkával állíthassuk!
  • Próbáljuk ki többféle bemeneti képekre!

Feladat

A Sudoku_cut.png képet forgassuk úgy, hogy a tábla szélei vízszintes és függőleges irány közelébe kerüljenek! A forgatási szöget a legerősebb egyes (legtöbb támogató pont vagy leghosszabb szakasz) megfelelő forgatása alapján számoljuk! Figyeljünk arra, hogy a forgatási szög értéke alapján az egyenes a vísszintes vagy függőleges irányhoz áll közelebb, annak megfelelően korrigáljuk!

A megoldáshoz ismerni kell a geometriai transzformációk használati módját.

Eredeti kép, vörös vonallal jelölve a legerősebb irányt:

A megoldás a legerősebb irányhoz tartozó 6 fokos elforgatással: