{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" }, "colab": { "name": "kl_py_kep_mod_openCV.ipynb", "provenance": [], "collapsed_sections": [], "include_colab_link": true } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "view-in-github", "colab_type": "text" }, "source": [ "\"Open" ] }, { "cell_type": "markdown", "metadata": { "id": "uSCNbGvH6U9M", "colab_type": "text" }, "source": [ "

\n", " \n", " \n", "

\n", "\n", "\n", "

\n", "\n", "\n", "\n", "# Python OpenCV \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": { "id": "eduZqbRG6U9O", "colab_type": "text" }, "source": [ "---\n", "\n", "WEB oldal : \n", "\n", "Hivatalos angol nyelvű elektronikus dokumentáció. \n", "\n", "Az OpenCV Python nyelvből való használatához szükséges csomagként való előzetes telepítése. \n", "\n", "Ha ez megtörtént, akkor a programunk elején beilleszthetjük a definíciókat az import paranccsal. Az OpenCV csomag neve például cv2, ezen keresztül érhetjük majd el a függvényeket és a definíciókat. \n", "\n", "A csomagban definiált __version__ nevű sztring változó az OpenCV verziószámát adja, amit a programunkban a print() függvénnyel ki is íratunk a konzolra.\n", "\n", "\n", "Digitális képek rendszerint külső fájlból érkeznek a programjainkba, ahol feldolgozzuk, és közben megjelenítjük őket. \n", "\n", "#### Kép beolvasásra az imread() függvény használható. \n", "\n", "Paraméterként a beolvasandó fájl nevét kell megadnunk. Ez tartalmazhat akár teljes vagy relatív elérési utat is. Ha ilyet nem adunk meg, akkor a képfájlnak a programunkkal azonos könyvtárban kell elhelyezkednie. \n", "\n", "Az elterjedt PNG és JPG fájlok kezelése támogatott. Az `imread()` függvény további paraméterezésével a szürkeárnyalatos konverziónál ismerkedünk majd meg.\n", "\n", "\n", "A beolvasás eredménye egy n-dimenziós Numpy tömb lesz. Ennek megkaphatjuk a dimenzióit a shape attribútumával.\n", "\n", "\n", "#### Az OpenCV képmegjelenítő függvénye az imshow(). A kép önálló ablakban jelenik meg.\n", "\n", "A függvény első paramétere egy sztring, amely egyrészt az ablak fejlécében lesz látható, másrészt ez fogja egyértelműen azonosítani az ablakot. \n", "\n", "\n", "Az OpenCV ugyanis egyszerre akár sok képablak kezelését is el tudja végezni, vagyis az azonosítás fontos. \n", "\n", "Ebből következik, hogy nem tudunk két egyező nevű ablakot létrehozni. \n", "\n", "Ha adott nevű ablak még nem létezik, akkor az OpenCV létrehozza és megjeleníti. Ha létezik, akkor a tartalmát cseréli le.\n", "\n", "A második paraméter a megjelenítendő, Numpy reprezentációjú kép.\n", "\n", "\n", "\n", "### További ablakkezelő függvények:\n", "\n", "Az `imshow()` függvény végrehajtása után a programunk futása rögtön folytatódik. \n", "\n", "A program kilépésekor minden ablak eltűnik. \n", "\n", "Emiatt szükséges, hogy egyes eredmények megjelenítése után várakozzunk a folytatásra. \n", "\n", "Legegyszerűbben a `waitKey()` függvénnyel tehetjük ezt meg. A nevéből kitalálható, hogy billentyűlenyomásra vár. Másrészt megadhatunk egy egész számértéket paraméterként, ami a maximális várakozási időt adja meg ezredmásodpercben. \n", "\n", "Ha addig nem történik billentyűlenyomás, akkor visszatér. A waitKey() visszaadja a lenyomott billentyű kódját is.\n", "\n", "\n", "\n", "### Az OpenCV számos függvényt biztosít a képmátrixok módosítására. \n", "\n", "Az egyik legegyszerűbb a kép tükrözése különféle középtengelyeire. Ezt a `flip()` függvény végzi. \n", " - Első paramétere a tükrözendő kép. \n", " - Második paramétere a tükrözés módját adja meg. Egész számot kell átadnunk. \n", "\n", "\n", "Ha értéke pozitív (példánkban 1), akkor a függőleges középtengelyre tükröz. 0 érték esetén a vízszintes középtengelyt használja, negatív érték esetén pedig mindkettőre tükröz. \n", "\n", "Eredménye egy új Numpy tömb lesz.\n", "\n", "Képet az `imwrite()` függvénnyel írhatunk ki fájlba. Első paramétere a kiírandó fájl neve, akár elérési úttal, hasonlóan a beolvasáshoz. A fájl formátumát a kiterjesztés alapján határozza meg. \n", "\n", "Célszerű png vagy jpg kiterjesztést választani. A második paraméter a kiírandó képet reprezentáló Numpy tömb.\n", "\n", "A `destroyAllWindows()` függvény az összes OpenCV képablakot bezárja. \n", "\n", "A program legvégén ezt célszerű használnunk, hogy érvényes visszatérési kóddal térjen vissza a programunk.\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "Yy0UOrr16U9P", "colab_type": "code", "colab": {} }, "source": [ "## betölt külső fájlból egy képet, megjeleníti, billentyűlenyomások után több tengely szerint tükrözi, \n", "## és fájlba menti az eredményt.\n", "\n", "# OpenCV modul definíciók importálása\n", "import cv2\n", "\n", "# OpenCV verziószám kiíratása\n", "print('OpenCV verzió:', cv2.__version__)\n", "\n", "# Kép beolvasása fájlból\n", "img = cv2.imread('kep_file.jpg')\n", "\n", "# Képméret kiíratása konzolra\n", "print(img.shape)\n", "\n", "# Kép megjelenítése ablakban\n", "cv2.imshow('image', img)\n", "cv2.waitKey(0)\n", "\n", "# Tükrözés a függőleges középtengelyre és megjelenítés\n", "flipped = cv2.flip(img, 1)\n", "cv2.imshow('image', flipped)\n", "cv2.imwrite('OpenCV-logo-flipped.png', flipped)\n", "cv2.waitKey(0)\n", "\n", "# Tükrözés mindkét középtengelyre és megjelenítés\n", "flipped2 = cv2.flip(img, -1)\n", "cv2.imshow('image', flipped2)\n", "cv2.waitKey(0)\n", "\n", "# Összes ablak bezárása\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "ItBKBQsX6U9S", "colab_type": "text" }, "source": [ "---\n", "\n", "Számos alacsony szintű képfeldolgozó művelet szürkeárnyalatos intenzitásképeken kerül definiálásra. \n", "\n", "Szürkeárnyalatos fotók esetén leggyakrabban a [0, 255] intenzitástartomány használt. \n", "\n", "\n", "A 0 érték jelenti a fekete, a 255 a fehér színt. A köztes értékek a feketéből fehérbe tartó szürke átmenetet jelentik.\n", "A fotóink, képeink rendszerint színesek, ezeket át kell alakítanunk szürkeárnyalatosra a fent említett műveletek végrehajtása előtt. \n", "\n", "\n", "Az OpenCV többféle lehetőséget is biztosít erre.\n", " - Az egyik megoldás, hogy már a kép betöltését szürkeárnyalatosként kérjük az IMREAD_GRAYSCALE értékkel. \n", " - Az imread() függvény második paramétere ezt szabályozza.\n", "\n", "\n", "\n", "### cv2.IMREAD_UNCHANGED\n", "A képmátrix típusát a fájl tartalma határozza meg. A színes képek 3 csatornás BGR mátrixként, a szürkekárnyalatosak 1 csatornás szürkeárnyalatos képként kerülnek beolvasásra. \n", "\n", "Mindenképpen ezt használjuk, ha 4 csatornás (RGBA) képet olvasunk be!\n", "\n", "\n", "\n", "### cv2.IMREAD_GRAYSCALE\n", "A beolvasott képmátrix 1 csatornás szürke lesz.\n", "\n", "\n", "### cv2.IMREAD_COLOR\n", "Ez az alapértelmezett működés.\n", "A beolvasott képmátrix 3 csatornás BGR mátrix lesz. Ha szürkeárnyalatos a bement, akkor mindhárom csatornára a beolvasott szürkeárnyalatos értékek kerülnek beállításra. \n", "\n", "Eltávolítja az esetleges további (például átlátszósági) csatornákat.\n", "\n", "\n", "További paramétermegadási lehetőségeket az OpenCV dokumentációban találunk az `ImreadModes` kulcsszónál.\n", "A másik lehetőség a cvtColor() függvény használata, ami számos színtér reprezentáció közötti átalakítást képes elvégezni. \n", "\n", "Megjegyezzük, hogy a szürkeárnyalatos fényességi érték (Y) a három színcsatorna (R, G, B) súlyozott átlagaként számítható az alábbi képlet szerint:\n", "\n", "Y=0.299⋅R+0.587⋅G+0.114⋅B\n", "\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "UnnxFFB56U9T", "colab_type": "code", "colab": {} }, "source": [ "## példaprogramban a szürkeárnyalatos konverzió használatát láthatjuk. \n", "## kiírjuk a konzolra a Numpy képmátrixok méretét is. \n", "# OpenCV modul definíciók importálása\n", "import cv2\n", "\n", "## -------------------------- V1\n", "# Kép beolvasása fájlból szürkeárnyalatosként\n", "imgr = cv2.imread('kep_file.jpg', cv2.IMREAD_GRAYSCALE)\n", "\n", "# Képméret kiíratása konzolra\n", "print(imgr.shape)\n", "\n", "# Kép megjelenítése ablakban\n", "cv2.imshow('image', imgr)\n", "cv2.waitKey(0)\n", "## -------------------------- V2\n", "# Kép beolvasása fájlból\n", "imgc = cv2.imread('kep_file.jpg', cv2.IMREAD_COLOR)\n", "print(imgc.shape)\n", "imgr2 = cv2.cvtColor(imgc, cv2.COLOR_BGR2GRAY)\n", "print(imgr2.shape)\n", "\n", "# Kép megjelenítése ablakban\n", "cv2.imshow('image', imgr2)\n", "cv2.waitKey(0)\n", "\n", "# felszabadítás\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "lG7Hw1vM6U9X", "colab_type": "text" }, "source": [ "---\n", "\n", "A számítógépek képernyőjén megjelenő színek a vörös, zöld és kék színkomponensek intenzitásának elegyítéséből állnak elő. A fotók betöltés után jellemzően ezen a három színcsatornán tárolódnak.\n", "\n", "\n", "Az RGB reprezentáció hasznos a digitális kijelzőn való megjelenítéshez. A színek leírására számos más színtér is létezik, amelyek bizonyos tulajdonságai jobbak az RGB-nél. Például a színkódok euklidészi távolsága (háromelemű vektorokként értelmezve őket) és a színérzet nem felel meg egymásnak az RGB színtérben, erre alkalmasabb a CIELab színtér. Nyomdai előkészítésnél a CMYK színtér használatos.\n", "\n", "\n", "Megjegyezzük, hogy a többcsatornás színes képek esetén a csatornák jellemző sorrendje a vörös (R), zöld (G), kék (B). Az OpenCV belső reprezentációja viszont a BGR sorrendet valósítja meg!\n", "\n", "Az OpenCV számos színtér reprezentációt ismer, és biztosítja az ezek közötti átalakításokat a cvtColor() függvénnyel.\n", "A két paramétere közül az első az átalakítandó képmátrix.\n", "A második az átalakítás módját jelenti, meghatározza, melyik színtérből melyikbe alakítunk. Néhány ilyen érték megtalálható az angol nyelvű dokumentációban.\n", "\n", "A függvény eredményeként az átalakított képet kapjuk.\n", "\n", "\n", "Példák színes->szürkeárnyalatos konverziós konstansokra:\n", "cv2.COLOR_BGR2GRAY, cv2.COLOR_GRAY2BGR, cv2.COLOR_RGB2GRAY, cv2.COLOR_GRAY2RGB\n", "\n", "Általánosan a cv2.COLOR_honnan2hova alakot használhatjuk, \n", " - ahol az egyik az RGB vagy BGR közül valamelyik, \n", " - a másik pedig lehet például XYZ, YCrCb, HSV, HLS, Lab, Luv, a kapcsolódó színtér elnevezéseknek megfelelően. \n", " \n", " Akár RGB->BGR és BGR->RGB átalakításra (csatornák sorrendjének cseréje) is lehetőség van.\n", " \n", " \n", "Eredményként az átalakított képet kapjuk.\n", "\n", "Figyeljünk arra, hogy ha egy szürkeárnyalatos kép alakítunk \"színessé\", a megjelenő kép továbbra is szürkeárnyalatos lesz, csak BGR színreprezentációban. \n", "\n", "Megjegyezzük, hogy egy többcsatornás képet színcsatornánként önálló egycsatornás képekké alakíthatjuk a cv2.split() függvénnyel. \n", "\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "y1012V8G6U9Y", "colab_type": "code", "colab": {} }, "source": [ "# OpenCV2 képbeolvasás, színtér konverzió minta\n", "\n", "# OpenCV modul definíciók importálása\n", "import cv2\n", "\n", "# Színes kép beolvasása fájlból\n", "imgc = cv2.imread('kep_file.jpg', cv2.IMREAD_COLOR)\n", "cv2.imshow('color', imgc)\n", "\n", "# Szürkeárnyalat\n", "imgr = cv2.cvtColor(imgc, cv2.COLOR_BGR2GRAY)\n", "cv2.imshow('grayscale', imgr)\n", "print('grayscale:', imgr.shape)\n", "cv2.waitKey(0)\n", "\n", "\n", "# Szürkéből \"színes\"\n", "imgc2 = cv2.cvtColor(imgr, cv2.COLOR_GRAY2BGR)\n", "cv2.imshow('gray2bgr', imgc2)\n", "print('gray2bgr:', imgc2.shape)\n", "cv2.waitKey(0)\n", "\n", "\n", "# Áttérés Lab színtérbe\n", "# L: szürkeárnyalat\n", "# a, b: kromatikusok (szín információ)\n", "imgLab = cv2.cvtColor(imgc, cv2.COLOR_BGR2Lab)\n", "print('imgLab:', imgLab.shape)\n", "cv2.waitKey(0)\n", "\n", "# Színcsatornákra bontás\n", "L, a, b = cv2.split(imgLab)\n", "cv2.imshow('L', L)\n", "cv2.imshow('a', a)\n", "cv2.imshow('b', b)\n", "cv2.waitKey(0)\n", "\n", "# felszabadítás\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "30IjS8cS6U9b", "colab_type": "text" }, "source": [ "---\n", "\n", "### A digitális képeket képmátrixban tároljuk. \n", "\n", "A mátrix elemei a képpontok, vagy az angol terminológiából átvéve a pixelek. A reprezentáció fontosabb tulajdonságai az alábbiak.\n", "\n", "A képpontokat sor (Y) és oszlop (X) indexeikkel címezhetjük.\n", "\n", "Az origó általában a bal felső sarok. Jellemzően ez a [0, 0] indexű elem. Egyes rendszerek, például a Matlab esetén ez az [1, 1].\n", "\n", "## Egy Numpy képmátrix elemének elérési módja:\n", "\n", "pixel = image[y, x]\n", "\n", "Vagyis [sor, oszlop] sorrendben kell megadni a koordinátákat. Az eredmény skalár érték (1 csatornás kép esetén) vagy 3 elemű tömb (BGR színes kép) lesz. A 3 elemű tömböt tovább lehet indexelni, például a vörös színkomponens elérése:\n", "R = image[y, x, 2]\n", "\n", "A Numpy tömb attribútumai információval szolgálnak többek között a mátrix dimenzióról (ndim), dimenziók szerinti méretéről (shape), és a elemtípusáról (dtype).\n", "\n", "Megjegyezzük, hogy ez a fajta képpont elérés rendkívül lassú, nagy mennyiségű adatelérést nem így érdemes csinálni. Használhatjuk a fejlettebb Numpy manipulációkat, vagy megírhatjuk a szükséges függvényeket C++-ban.\n", "\n", "\n", "A Numpy képmátrix reprezentációban az értékadás (az = operátorral) referencia szerint történik! \n", "\n", "Ez azt jelenti, hogy onnantól kezdve mindkét változó ugyanarra a képmátrixra hivatkozik, az egyiken keresztüli változás a másikra is hatással van! \n", "\n", "Ha valódi másolatot szeretnénk készíteni egy képmátrixról, akkor hívjuk meg a mátrix objektum copy() függvényét! \n", "\n", "\n", "A referencia takarékosabb a memóriával, de például változás követés esetén muszáj lehet másolatot készíteni.\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "_ml8sEW36U9c", "colab_type": "code", "colab": {} }, "source": [ "## A példánkban az image és az image_ref ugyanarra képmátrixra fog hivatkozni. \n", "## Az image_copy egy új képmátrixot kap, az eredetivel megegyező tartalommal.\n", "\n", "\n", "# OpenCV modul definíciók importálása\n", "import cv2\n", "\n", "# esemény k ezelő (egér klikkelés)\n", "def mouse_click(event, x, y, flags, param):\n", " # Globalis valtozo atvetele\n", " global image\n", "\n", " if event == cv2.EVENT_LBUTTONDOWN:\n", " # (x, y) színérték kiírása\n", " print('Pixel = ', image[y, x])\n", " # Ha 3 csatornás a kép\n", " if image.ndim == 3:\n", " print('R = ', image[y, x, 2])\n", " cv2.imshow('image', image)\n", "\n", "# kép állomány kiválasztása\n", "image = cv2.imread('kep_file.jpg', cv2.IMREAD_COLOR) ## szines\n", "# image = cv2.imread('kep_file.jpg', cv2.IMREAD_GRAYSCALE) ## Szűrke\n", "\n", "# kép aadatok megjelenítése\n", "print('Kép dimenziói: ', image.ndim)\n", "print('Kép mérete:', image.shape)\n", "print('Kép pixeltípusa: ', image.dtype)\n", "\n", "# Kép megjelenítése\n", "cv2.imshow('image', image)\n", "\n", "# a képpontok elérését mutatja be. Betöltünk egy képet, megjelenítjük ablakban, \n", "# valamint az ablakhoz egy OpenCV egéresemény-kezelőt kapcsolunk\n", "# Egerkezelo callback fuggveny beallitasa az ablakhoz\n", "cv2.setMouseCallback('image', mouse_click)\n", "\n", "## Ha az image nevű képmegjelenítő ablakban kattintunk vagy mozgunk az egérrel, \n", "## akkor a rendszer meghívja a mouse_click() függvényünket, megfelelő paraméterezéssel.\n", "\n", "# Kilepes billentyunyomasra\n", "cv2.waitKey(0)\n", "\n", "# felszabadítás\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "velVMfSm6U9f", "colab_type": "text" }, "source": [ "---\n", "\n", "\n", "## Numpy részkép kivágása és visszamásolása\n", "\n", "\n", "A Numpy lehetőséget ad a mátrixok szeletelésére, vagyis indextartományokkal megadott részeinek elérésére. \n", "\n", "A szeletelés eredménye közvetlenül felhasználható, vagy új változóban eltárolható.\n", "\n", "Fontos, hogy ez a részmátrix továbbra is az eredeti mátrixra hivatkozik, változtatása az eredetin is megjelenik! \n", "\n", "Ha tényleges másolatot szeretnék, akkor használjuk a copy() függvényt!\n", "\n", "Az indextartományok megadásánál az adott dimenzióra vonatkozó kezdő és záró koordinátát kell megadni, kettősponttal elválasztva. \n", "\n", "Fontos! A záró koordináta érték már nem vesz részt a kivágásban! A következő példa egy 90x90 méretű eredményt ad.\n", "\n", "cropped = image[82:172, 396:486]\n", "\n", "\n", "Az intervallum megadásakor (a kettőspont előtt/után) a kezdő és/vagy a záró koordináta érték elhagyható. Ekkor az adott dimenzió szerinti minimális, illetve maximális koordináta értéket veszi a rendszer. \n", "\n", "\n", "Például az alábbi értékadással ki tudjuk nullázni a cropped mátrix minden elemét.\n", "cropped[:, :] = 0\n", "\n", "\n", "Jobbról balra haladva a teljes dimenziót felhasználó indexeket elhagyhatjuk.\n", "\n", "\n", "Egy kisebb mátrixot be tudunk másolni egy nagyobba. A nagyobbnál indextartományokat kell megadni.\n", "\n", "(A kisebbnél is lehet.) A két (rész)mátrix méretnek meg kell egyeznie egymással!\n", "\n", "\n", "image[10:100, 20:110] = cropped\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "GThaxrWm6U9g", "colab_type": "code", "colab": {} }, "source": [ "# OpenCV modul definíciók importálása\n", "import cv2\n", "\n", "\n", "image = cv2.imread('gyava_oroszlan.jpg')\n", "\n", "# Képkivágás; sor és oszlop tartomány megadása\n", "cropped = image[82:172, 396:486]\n", "\n", "# Kivágott rész képbe másolása, új helyre\n", "image[10:100, 20:110] = cropped\n", "\n", "cv2.imshow('image', image)\n", "cv2.imshow('cropped', cropped)\n", "\n", "cv2.waitKey(0)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "cLDCz65c6U9j", "colab_type": "text" }, "source": [ "---\n", "\n", "## Színcsatornák szétválasztása és egyesítése\n", "\n", "\n", "Egy többcsatornás kép szétbontható különálló intenzitás csatornákra az OpenCV split() függvényével. \n", "\n", "Eredményképpen egy felsorolási (tuple) típusú Python objektumot kapunk, amely BGR színes kép esetén három Numpy tömböt sorol fel. \n", "\n", "#### Az eredményt átvenni így tudjuk:\n", "imgc = cv2.imread('GolyoAlszik_rs.jpg', cv2.IMREAD_COLOR)\n", "b, g, r = cv2.split(imgc)\n", "\n", "\n", "Ne felejtsük el, hogy az OpenCV BGR sorrendben tárolja a színcsatornákat! \n", "\n", "Ezután a b, g és r tömbökkel mint szürkeárnyalatos, egycsatornás képekkel dolgozhatunk tovább.\n", "\n", "\n", "#### Színes képet csatornák egyesítésével kaphatunk, amit az OpenCV merge() függvénye biztosít:\n", "imgc2 = cv2.merge((b, g, r))\n", "\n", "\n", "A látszat ellenére a függvénynek egy paramétere van, a csatornák felsorolása tuple objektumként, emiatt kell kettős zárójelezés, ahogyan színkódolás mutatja.\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "PeexZdM46U9k", "colab_type": "code", "colab": {} }, "source": [ "# Modul definíciók importálása\n", "import cv2\n", "\n", "# Kép beolvasása fájlból\n", "imgc = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_COLOR)\n", "cv2.imshow('color', imgc)\n", "\n", "# Szürkeárnyalat\n", "imgr = cv2.cvtColor(imgc, cv2.COLOR_BGR2GRAY)\n", "cv2.imshow('grayscale', imgr)\n", "cv2.waitKey(0)\n", "\n", "# Színcsatornákra bontás és megjelenítés\n", "b, g, r = cv2.split(imgc)\n", "cv2.imshow('red', r)\n", "cv2.imshow('green', g)\n", "cv2.imshow('blue', b)\n", "cv2.waitKey(0)\n", "\n", "# Vörös csatorna nullázása és BGR kép előállítása\n", "r[:, :] = 0\n", "imgc2 = cv2.merge((b, g, r))\n", "cv2.imshow('0,g,b', imgc2)\n", "cv2.waitKey(0)\n", "\n", "# Színcsatorna ablakok bezárása\n", "cv2.destroyWindow('red')\n", "cv2.destroyWindow('green')\n", "cv2.destroyWindow('blue')\n", "cv2.destroyWindow('0,g,b')\n", "\n", "# Áttérés Lab színtérbe\n", "# L: szürkeárnyalat\n", "# a, b: kromatikusok (szín információ)\n", "imgLab = cv2.cvtColor(imgc, cv2.COLOR_BGR2Lab)\n", "L, a, b = cv2.split(imgLab)\n", "cv2.imshow('L', L)\n", "cv2.imshow('a', a)\n", "cv2.imshow('b', b)\n", "cv2.waitKey(0)\n", "\n", "# felszabadítás\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "1hqlsyj-6U9n", "colab_type": "code", "colab": {} }, "source": [ "# Modul definíciók importálása\n", "import numpy as np\n", "import cv2\n", "\n", "# 320x200x3 méretű Numpy tömb létrehozása RGB színes képnek\n", "img = np.ndarray((200, 320, 3), np.uint8)\n", "\n", "\n", "# Feltöltés 192 (világosszürke) színnel\n", "img.fill(192)\n", "\n", "# Kör rajzolása az (50, 100) középponttal, 40 sugárral, vörös színnel\n", "cv2.circle(img, (50, 100), 40, (0, 0, 192), -1)\n", "\n", "##Vonal\n", "cv2.line(img, (55, 55), (88, 88), (20, 30, 150), 2)\n", "\n", "## Téglalap\n", "cv2.rectangle(img, (25, 25), (91, 91), (150, 20, 30), 2)\n", "\n", "## Ellipszis\n", "#cv2.ellipse(img, (77, 77), (200, 200), 10, 10, (100, 10, 10), 3)\n", "\n", "## A főtengely szögének kezdőállása az X-tengely iránya. A pozitív körüljárási irány az óramutató járásával ellentétes. \n", "## Az értékek fokban értelmeződnek. A kezdőszög és zárószög megadásával elliptikus ívet rajzolhatunk.\n", "\n", "## Szöveg elhelyezése\n", "## cv2.putText(img, 'Szoveg', (20, 20), FONT_HERSHEY_SIMPLEX, 24, (50, 150, 50), v3, vonaltipus)\n", "\n", "\n", "\n", "# Kép megjelenítése ablakban\n", "cv2.imshow('image', img)\n", "cv2.waitKey(0)\n", "\n", "# Kép mentése fájlba\n", "cv2.imwrite('ocv_test1_out.png', img)\n", "\n", "# Összes ablak bezárása\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "0R97Etel6U9q", "colab_type": "text" }, "source": [ "---\n", "\n", "\n", "## Képek tárolása fájlban\n", "\n", "A képeink gyakran fájlből kerülnek beolvasásra, illetve az eredmények fájlba íródnak ki. \n", "\n", "Mint láttuk a képmátrix tárolása képmátrixban, számértékekkel történik, így a fájlba írás megoldható.\n", "\n", "Figyelnünk célszerű viszont a képi adat tárolási mennyiségigényére. Például egy 24 megapixeles (24 millió képpont érzékelésére képes szenzorral rendelkező) kamerával készült kép 3 bájtos RGB képpont-kódolást feltételezve 24 x 3 = 72 megabájtot foglal. 15 darab ilyen képpel már 1 GB felett járunk!\n", "\n", "A fájlban történő tároláskor ezért célszerű kihasználni a képi adatban található redundanciákat. Például szöveget ábrázoló képernyőképek esetén jellemzően nagyméretű homogén, vagyis egyforma színnel rendelkező képpont területek találhatók. Futáshossz kódolással ezt tömörebben leírhatjuk. \n", "\n", "Egy másik megközelítés esetén a mátrixban gyakrabban előforduló számértékek rövidebb (akár 1 bites), a ritkán előfordulók hosszabb (sok bitből álló) kódszavat kapjanak. \n", "\n", "Ilyet állíthatunk elő például a Huffman kódolással. Fotók esetén az úgynevezett transzformációs kódolások a népszerűek (DCT, wavelet). Ezek nagymértékű méretcsökkenést képesek okozni, viszont ekkor a képmátrix eredeti állapota nem, csak egy közelítése állítható vissza. Ezt a megközelítést veszteséges tömörítésnek nevezik.\n", "\n", "\n", "\n", "### Veszteségmentes tömörítés\n", "\n", "Pontosan visszakapjuk a képmátrix elemeinek intenzitás- vagy színértékeit.\n", "Nagyobb fájlméret.\n", "Vektoros vonalrajzokról, szövegekről, képernyőképekről készült képek, valamint orvosi képek esetén használatos.\n", "Néhány ilyen fájlformátum: PNG, RAW, TIFF, BMP, DICOM.\n", "\n", "\n", "#### Veszteséges tömörítés\n", "\n", "Kisebb-nagyobb eltérések előfordulhatnak az eredeti szín/intenzitásértékekhez képest, vagyis nem pontosan ugyanazt a színértéket kapják a kitömörítés után, mint ami eredetileg volt.\n", "Sokkal kisebb fájlméret. Állítható veszteségaránnyal dolgozhatunk.\n", "Fotók esetén javasolt.\n", "\n", "De-facto szabvány formátum: JPEG. A legtöbb digitális kamera hardver szinten támogatja.\n", "Nyers (RAW) formátum jellemzői (veszteségmentes, profibb eszközökön)\n", "\n", "„Előhívatlan”, nyers adat, az érzékelő által mért közvetlen számértékek.\n", "Geometriai korrekció nélkül (például a lencsetorzítás hatása látható).\n", "\n", "\n", "Az expozíció bizonyos paraméterei utólagosan állíthatók (színhőmérséklet, világosság, ...).\n", "\n", "\n", "#### Az OpenCV számos függvényt definiál képek közötti és skalár értékekkel való aritmetikára és logikai műveletekre. \n", "\n", "absdiff(): abszolút különbség.\n", "\n", "add(): összeadás.\n", "\n", "addWeighted(): súlyozott összeadás.\n", "\n", "bitwise_and(), bitwise_not(), bitwise_or(), bitwise_xor(): bitenként számolt logikai ÉS, NEM, VAGY, KIZÁRÓ VAGY.\n", "\n", "divide(): osztás.\n", "\n", "exp(): exponens.\n", "\n", "log(): logaritmus.\n", "\n", "min(), max(): képpontonkénti minimum, maximum (az eredmény képmátrix lesz).\n", "\n", "multiply(): szorzás.\n", "\n", "normalize(): értékkészlet normalizálás.\n", "\n", "pow(): hatványozás.\n", "\n", "scaleAdd(): skálázott összeadás.\n", "\n", "subtract(): kivonás.\n", "\n", "\n", "### Egy képmátrixra statisztika számítás, skalár eredménnyel:\n", "\n", "mean(): átlag.\n", "\n", "meanStdDev(): átlag és szórás.\n", "\n", "minMaxIdx(): minimum és maximum értéke és indexe.\n", "\n", "sum(): összeg.\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": { "id": "LJtBtRpE6U9r", "colab_type": "text" }, "source": [ "---\n", "\n", "\n", "Maszkok segítségével meg tudjuk határozni, hogy a kép mely részletének feldolgozása fontos a számunkra.\n", "\n", "A maszk jellemzően egy bináris vagy szürkeárnyalatos képnek tekinthető, ahol az intenzitásértékek jelzik a kapcsolódó képpontok tulajdonságait: része-e az érdekes régiónak (nullától különböző, például 1 vagy maximális érték) vagy sem (0 érték), amennyiben igen, milyen mértékben? A maszk kép mérete megegyezik a kapcsolódó kép méretével.\n", "\n", "Maszkokat tudunk létrehozni manuális rajzolással vagy szegmentáló algoritmusokkal, amelyekkel később foglalkozunk. \n", "\n", "Jelenleg elfogadjuk, hogy ilyenek rendelkezésre állnak, és azt vizsgáljuk, hogyan tudunk ezekkel dolgozni.\n", "\n", "A maszkokkal való munka általános megközelítése, ha for ciklusokkal bejárjuk a teljes képet, és képpontonként ellenőrizzük a kritériumot. \n", "\n", "Látni fogjuk, hogy ez rendkívül lassú, helyette célszerű Numpy vagy OpenCV aritmetikával dolgozni! A sebességkülönbség több százszoros is lehet!\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "ULTsvtDF6U9s", "colab_type": "code", "colab": {} }, "source": [ "mport cv2\n", "import time\n", "\n", "# Kép, maszk és maszk körvonal beolvasása.\n", "img = cv2.imread('car_numberplate_rs.jpg', cv2.IMREAD_COLOR)\n", "mask = cv2.imread('car_numberplate_rs_mask.png', cv2.IMREAD_COLOR)\n", "edge = cv2.imread('car_numberplate_rs_mask_edge.png', cv2.IMREAD_GRAYSCALE)\n", "\n", "# Képmátrix méretek ellenőrzése. Az assert után egy feltételt adhatunk meg. \n", "# Ha az nem teljesül, akkor a program futása hibaüzenttel megszakad. \n", "# A shape attribútum első két elemét nézzük, ami a sorok és oszlopok számát veszi csak figyelembe.\n", "\n", "assert img.shape[0:2] == mask.shape[0:2]\n", "assert img.shape[0:2] == edge.shape[0:2]\n", "Kép és maszk megjelenítése.\n", "\n", "cv2.imshow('img', img)\n", "cv2.imshow('mask', mask)\n", "\n", "# Színes képen a maszkon kívüli terület kinullázása és megjelenítése. Mivel a feltételezzük, \n", "# hogy a maszk kép 0 és 255 értékeket tartalmaz, a logikai ÉS művelet a maszkon belüli, \n", "# vagyis a 255 értékkel fedésbe kerülőket tartja meg, a többit nullázza.\n", "\n", "masked = cv2.bitwise_and(img, mask)\n", "cv2.imshow('masked', masked)\n", "cv2.waitKey(0)\n", "\n", "# Eredeti színes képen a maszkon belüli rész fehér színűre változtatása.\n", "\n", "img_roi = img.copy()\n", "img_roi[mask > 0] = 255\n", "cv2.imshow('img_roi', img_roi)\n", "cv2.waitKey(0)\n", "\n", "# Vörös színű maszk körvonal elhelyezése az eredeti képen. \n", "# Kettős for ciklussal bejárjuk a maszkot, a 255 értékek helyén az eredeti képmátrixban vörös színt állítunk be. \n", "#Vegyük észre, hogy nem elegendő a vörös csatornára értéket írni, \n", "# a másik kettő kinullázása is szükséges a korrket vörös szín előállításához! \n", "#A ciklust időméréssel látjuk el.\n", "\n", "img_edge = img.copy()\n", "start_time = time.perf_counter()\n", "for y in range(0, img.shape[0]):\n", " for x in range(0, img.shape[1]):\n", " if edge[y, x] > 0:\n", " img_edge[y, x] = [0, 0, 255]\n", "\n", "end_time = time.perf_counter()\n", "print((end_time - start_time) * 1000.0, \"ezredmásodperc.\")\n", "cv2.imshow('img_edge', img_edge)\n", "cv2.waitKey(0)\n", "\n", "# Vörös színű körvonal rávetítés OpenCV logikai függvényekkel, időméréssel. \n", "# A sokkal gyorsabb működés miatt célszerű így eljárnunk! Kb. 250-szeres gyorsulás mérhető!\n", "\n", "# A működésének a lényege, hogy az eredeti BGR képet csatornákra bontjuk, a vörös csatornába \n", "# a VAGY művelettel beégetjük a vörös körvonalat (ahol a maszk körvonal kép 255 értékű, \n", "# ott a vörös csatorna is ezt az értéket veszi fel, ahol a körvonal 0 értékű, \n", "# ott megmarad az eredetileg ott szereplő érték), a másik két csatornán nullázzuk ugyanezen helyeket\n", "# (a ~ operátor a bitenkénti negálás, vagyis ellentettre fordítás, \n", "# ezzel ÉS-elve kapjuk a körvonal pontokban a nulla eredményt).\n", "\n", "img_ocv_edge = img.copy()\n", "start_time = time.perf_counter()\n", "b, g, r = cv2.split(img_ocv_edge)\n", "r = cv2.bitwise_or(r, edge)\n", "g = cv2.bitwise_and(g, ~edge)\n", "b = cv2.bitwise_and(b, ~edge)\n", "img_ocv_edge = cv2.merge((b, g, r))\n", "end_time = time.perf_counter()\n", "print((end_time - start_time) * 1000.0, \"ezredmásodperc.\")\n", "\n", "cv2.imshow('img_ocv_edge', img_ocv_edge)\n", "cv2.waitKey(0)\n", "\n", "# Program szabályos lezárása.\n", "cv2.destroyAllWindows()\n", "\n" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "KNHOWyl96U9v", "colab_type": "text" }, "source": [ "---\n", "\n", "## Egycsatornás hisztogram számítása és megjelenítése\n", "\n", "Az OpenCV a calcHist() függvénnyel biztosítja a hisztogram meghatározást.\n", "\n", "hist = calcHist([images], [channels], mask, [histSize], [ranges])\n", "\n", "Eredményképpen egy NumPy tömböt kapunk (hist).\n", "\n", "Paraméterként meg kell adnunk a képmátrixo(ka)t Python tömbként felsorolva ([images]); a felhasználandó csatornák indexeit, szintén Python tömbként ([channels]); egy maszkot (mask), ha a kép csak egy részére végeznénk el a számítást (None, ha a teljes kép érdekes); a hisztogram rekeszeinek számát ([histSize]), valamint az intenzitástartományt ([ranges]). Amennyiben az intenzitástartomány bővebb, mint a megadott hisztogram rekeszek száma, akkor a függvény összevon intenzitásértékeket.\n", "\n", "Az eredmény egy histSize méretű NumPy oszlopvektor lesz. Ezt grafikonon ábrázolhatjuk például a matplotlib csomag pyplot alcsomagjával, aminek a definícióit plt néven illesztjük be a forráskódba.\n", "\n", "A figure() függvényével kezdhetünk új ábrát rajzolni, megadva a méreteit.\n", "\n", "Az ábrazolandó adatot többféle módon jeleníthetjük meg.\n", "\n", "A vlines() függvény függőleges vonallal ábrázolja az értékeket. Egycsatornás hisztogram ábrázolására általános esetben a legjobban használható.\n", "A plot() függvénnyel a függvényértékek egyenes vonallal kerülnek összekötésre. Többcsatornás hisztogram ábrázolásra megfelelő, csatornánként különböző színnel, amennyiben nincs nagy \"ugrás\" a szomszédos intenzitásértékek előfordulási gyakoriságában.\n", "A scatter() segítségével pontfelhőként rajzolhatjuk ki. Többcsatornás hisztogram ábrázolásoknál hasznos, ahol például valamilyen hisztogram művelet hatására több intenzitásérték is 0 gyakorisággal fordul elő, így függvényként plot()-tal nem ábrázolható szépen.\n", "A bar() oszlopdiagramot rajzol. Használata kisebb darabszámú hisztogramok esetén célszerű. Túl sűrű megjelenítésnél az oszlopok átfedik egymást.\n", "Az xlim() és ylim() az X és Y tengelyek menti ábrázolandó minimum és maximum értékeket adja meg.\n", "\n", "A show() jeleníti meg az előkészített ábrát. A program futása felfüggesztődik, míg az ábrát be nem zárjuk.\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "GDV6HekF6U9w", "colab_type": "code", "colab": {} }, "source": [ "import cv2\n", "import numpy as np\n", "from matplotlib import pyplot as plt\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_GRAYSCALE)\n", "\n", "cv2.imshow('img', img)\n", "print('Min: {}'.format(np.min(img)))\n", "print('Max: {}'.format(np.max(img)))\n", "\n", "# 256 elemű hisztogram\n", "hist_gray = cv2.calcHist([img], [0], None, [256], [0, 256])\n", "print(hist_gray.shape)\n", "\n", "plt.figure(figsize=(4,2), dpi=100)\n", "# plt.plot(hist_gray)\n", "# plt.scatter(np.arange(256), hist_gray, s=1)\n", "# plt.bar(np.arange(256), np.transpose(hist_gray.astype(int))[0])\n", "plt.vlines(np.arange(256), 0, hist_gray)\n", "# plt.xlim([0, 255])\n", "plt.ylim([0, np.max(hist_gray)])\n", "plt.tight_layout(pad=0)\n", "plt.show()\n", "\n", "# 16 elemű hisztogram\n", "hist_gray2 = cv2.calcHist([img], [0], None, [16], [0, 256])\n", "print(hist_gray2.shape)\n", "\n", "plt.figure(figsize=(4,2), dpi=100)\n", "# plt.plot(hist_gray2)\n", "# plt.scatter(np.arange(16), hist_gray2, s=5)\n", "# plt.bar(np.arange(16), np.transpose(hist_gray2.astype(int))[0])\n", "plt.vlines(np.arange(16), 0, hist_gray2)\n", "# plt.xlim([0, 15])\n", "plt.ylim([0, np.max(hist_gray2)])\n", "plt.show()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "xDjtnVH-6U9z", "colab_type": "code", "colab": {} }, "source": [ "# Többcsatornás képek esetén vagy egycsatornásra alakítunk, vagy csatornánként készítünk hisztogramot. \n", "# Ezeket külön, vagy akár egy diagramban is ábrázolhatjuk, ahogyan az alábbi példában láthatjuk.\n", "\n", "import cv2\n", "import numpy as np\n", "from matplotlib import pyplot as plt\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_COLOR)\n", "\n", "cv2.imshow('img', img)\n", "\n", "hist_b = cv2.calcHist([img], [0], None, [256], [0, 256])\n", "hist_g = cv2.calcHist([img], [1], None, [256], [0, 256])\n", "hist_r = cv2.calcHist([img], [2], None, [256], [0, 256])\n", "\n", "img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n", "hist_gray = cv2.calcHist([img_gray], [0], None, [256], [0, 256])\n", "\n", "plt.figure(figsize=(4,2), dpi=100)\n", "plt.vlines(np.arange(256), 0, hist_r, color='r')\n", "# plt.xlim([0, 255])\n", "plt.ylim([0, np.max(hist_r)])\n", "plt.show()\n", "\n", "plt.figure(figsize=(4,2), dpi=100)\n", "plt.vlines(np.arange(256), 0, hist_g, color='g')\n", "# plt.xlim([0, 255])\n", "plt.ylim([0, np.max(hist_g)])\n", "plt.show()\n", "\n", "plt.figure(figsize=(4,2), dpi=100)\n", "plt.vlines(np.arange(256), 0, hist_b, color='b')\n", "# plt.xlim([0, 255])\n", "plt.ylim([0, np.max(hist_b)])\n", "plt.show()\n", "\n", "plt.figure(figsize=(4,2), dpi=100)\n", "plt.plot(hist_b, color='b')\n", "plt.plot(hist_g, color='g')\n", "plt.plot(hist_r, color='r')\n", "plt.plot(hist_gray, color='k')\n", "# plt.xlim([0, 255])\n", "plt.ylim([0, max(np.max(hist_b), np.max(hist_g), np.max(hist_r))])\n", "plt.show()\n", "# RGB hisztogram a 0 és 255 elemek kihagyásával\n", "plt.figure(figsize=(4,2), dpi=100)\n", "plt.plot(hist_b[1:255], color='b')\n", "plt.plot(hist_g[1:255], color='g')\n", "plt.plot(hist_r[1:255], color='r')\n", "plt.plot(hist_gray, color='k')\n", "# plt.xlim([0, 255])\n", "plt.ylim([0, max(np.max(hist_b[1:255]), np.max(hist_g[1:255]), np.max(hist_r[1:255]))])\n", "plt.show()\n" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "ZPh8qFtN6U91", "colab_type": "text" }, "source": [ "---\n", "\n", "## Éldetektálás\n", "\n", "Él ott található a képen, ahol a lokális intenzitáskülönbség nagy.\n", "\n", "A képfüggvény deriváltja egy adott pontban mutatja a legnagyobb csökkenés irányát, ami merőleges az élre (ha van az adott pontban), illetve a gradiensvektor nagysága jellemzi az él erősségét.\n", "\n", "A képen éleket lehet az első- vagy másodrendű deriváltak számításával keresni. Mindkettő nagyon zajérzékeny, ezért célszerű előzőleg képsimítást végezni a zaj hatásának csökkentésére.\n", "\n", "Mivel a digitális képek diszkrétek, ezért a deriváltszámítás is diszkrét lesz. Jellemzően X- és Y-irányú parciális deriváltakat közelítünk konvolúciós kernelekkel.\n", "\n", "A simító és parciális deriváltat közelítő konvolúciók általában összevonhatók közös kernelbe, így egy lépésen végrehajthatók.\n", "\n", "Egy jól használható éldetektor további feltételezésekkel is él, ilyen például a Canny detektor (simítás, deriválás, nem maximális élek elnyomása, hiszterézis küszöbölés).\n", "\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "BLxjohyN6U92", "colab_type": "code", "colab": {} }, "source": [ "import cv2\n", "import numpy as np\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_GRAYSCALE)\n", "\n", "Gx = np.array([\n", "[-1, 0, 1],\n", "[-2, 0, 2],\n", "[-1, 0, 1]])\n", "\n", "imgGx = cv2.filter2D(img, cv2.CV_32F, Gx)\n", "imgGxNorm = cv2.normalize(imgGx, None, 0, 1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32FC1)\n", "cv2.imshow('Gx', imgGxNorm)\n", "\n", "Gy = np.array([\n", "[1, 2, 1],\n", "[0, 0, 0],\n", "[-1, -2, -1]])\n", "\n", "imgGy = cv2.filter2D(img, cv2.CV_32F, Gy)\n", "imgGyNorm = cv2.normalize(imgGy, None, 0, 1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32FC1)\n", "cv2.imshow('Gy', imgGyNorm)\n", "\n", "imgGMagn = cv2.magnitude(imgGx, imgGy)\n", "imgGMagnNorm = cv2.normalize(imgGMagn, None, 0, 1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32FC1)\n", "cv2.imshow('Gradient magnitude', imgGMagnNorm)\n", "\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "9XbZEMgN6U95", "colab_type": "code", "colab": {} }, "source": [ "import cv2\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_GRAYSCALE)\n", "\n", "blurred = cv2.GaussianBlur(img, (5, 5), 2.0)\n", "edges = cv2.Canny(blurred, 100, 200, None, 5, True)\n", "\n", "cv2.imshow('Canny', edges)\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "K2hm2YyA6U98", "colab_type": "code", "colab": {} }, "source": [ "## Bementi színes kép átméretezése megadott szorzótényezőkkel, többféle interpolációs technikával.\n", "\n", "import cv2\n", "\n", "par_fx = 3\n", "par_fy = 1.5\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_COLOR)\n", "\n", "res_nearest = cv2.resize(img, None, fx=par_fx, fy=par_fy, interpolation=cv2.INTER_NEAREST)\n", "res_linear = cv2.resize(img, None, fx=par_fx, fy=par_fy, interpolation=cv2.INTER_LINEAR)\n", "res_area = cv2.resize(img, None, fx=par_fx, fy=par_fy, interpolation=cv2.INTER_AREA)\n", "res_cubic = cv2.resize(img, None, fx=par_fx, fy=par_fy, interpolation=cv2.INTER_CUBIC)\n", "res_lanczos4 = cv2.resize(img, None, fx=par_fx, fy=par_fy, interpolation=cv2.INTER_LANCZOS4)\n", "\n", "cv2.imshow('Original', img)\n", "cv2.imshow('Resampled nearest', res_nearest)\n", "cv2.imshow('Resampled linear', res_linear)\n", "cv2.imshow('Resampled area', res_area)\n", "cv2.imshow('Resampled cubic spline', res_cubic)\n", "cv2.imshow('Resampled Lanczos', res_lanczos4)\n", "\n", "cv2.waitKey(0)\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "x5wI6-y56U-A", "colab_type": "code", "colab": {} }, "source": [ "# Kép átméretezése explicit új képméret megadásával. A skálatényezők a képarányokból adódnak, \n", "# amiket ellenőrzésképpen ki is írunk a konzolra. \n", "# Figyeljünk arra, hogy az OpenCV és a Numpy más sor/oszlop sorrendet használ!\n", "\n", "\n", "import cv2\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_COLOR)\n", "print(img.shape)\n", "\n", "dsize = (200, 100)\n", "dst = cv2.resize(img, dsize, interpolation=cv2.INTER_AREA)\n", "\n", "fx = dsize[0] / img.shape[1]\n", "fy = dsize[1] / img.shape[0]\n", "\n", "print('fx =', fx)\n", "print('fy =', fy)\n", "\n", "cv2.imshow('Original', img)\n", "cv2.imshow('Resampled area', dst)\n", "\n", "cv2.waitKey(0)\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "Agz1jVTU6U-D", "colab_type": "code", "colab": {} }, "source": [ "# Képmátrix eltolása. Az eltolás hatást manuális transzformációs mátrix létrehozással oldjuk meg.\n", "\n", "import cv2\n", "import numpy as np\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', 0)\n", "rows, cols = img.shape\n", "\n", "M = np.float32([[1, 0, 100], [0, 1, 50]])\n", "dst = cv2.warpAffine(img, M, (cols, rows))\n", "\n", "cv2.imshow('img', dst)\n", "cv2.waitKey(0)\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "JZ6eKO_R6U-F", "colab_type": "code", "colab": {} }, "source": [ "# Kép forgatása a középpontja körül. A forgatási értéket csúszkával állíthatjuk.\n", "\n", "import numpy as np\n", "import cv2\n", "\n", "\n", "def ontrackbar(x):\n", " M = cv2.getRotationMatrix2D((cols / 2, rows / 2), x, 1)\n", " dst = cv2.warpAffine(img, M, (cols, rows))\n", " cv2.imshow('image', dst)\n", "\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg', 0)\n", "rows, cols = img.shape\n", "cv2.namedWindow('image')\n", "\n", "cv2.createTrackbar('R', 'image', 0, 360, ontrackbar)\n", "ontrackbar(0)\n", "\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyWindow('image')" ], "execution_count": 0, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "SF1Vw3U-6U-I", "colab_type": "text" }, "source": [ "---\n", "\n", "## Alakzat detekció\n", "\n", "\n", "Az eddigi fejezetkben olyan műveleteket ismertünk meg, amelyek képi bementből képi kimenet készítettek, beleértve a szegmentálást, az éldeketálást. \n", "\n", "A következő lépésben képből struktúrális leírást szeretnénk kinyerni, ezzel elindulva a képtartalom értelmezésének irányába.\n", "\n", "### Célok\n", " - Képből alakzatokat leíró geometriai jellemzők kinyerése\n", " - Egyenes vonalszakaszok, körök, négyszögek, ...\n", " - Vékonyítás\n", " - Objektumok határának leírása (kontúr reprezentáció)\n", " \n", "### Összefüggő komponensek keresése bináris képen\n", " - A geometriai leírás alapján alakzatot jellemző értékek, vektorok számítása\n", " - Kerület, terület, befoglaló téglalap, konvex burok, ...\n", " - Körszerűség, kompaktság, ...\n", " - Topológiai leírás, Euler-szám, váz, ...\n", " - Momentumok\n", "\n", "### Módszerek\n", " - Egyenesek, körök detektálása: Hough-transzformáció\n", " - Célszerű a képen előzetesen élkeresést végrehajtani\n", " - Vékonyítás: Zhang-Suen és Gua-Hall módszerekkel\n", " - Kontúr reprezentáció: lánckód, kontúrpontok sorozata\n", " - Alakjellemző-alapú hasonlóság: momentumok\n", "\n", "---" ] }, { "cell_type": "code", "metadata": { "id": "hDJKou3T6U-J", "colab_type": "code", "colab": {} }, "source": [ "# Figyeljük meg, hogyan rajzolhatjuk a képre a detektált egyeneseket! \n", "# A rho és theta paraméterek alapján meghatározásra kerül az egyenes egy pontja. \n", "# Erről balra és jobbra olyan távolságra választunk pontokat, amelyek feltehetőleg a képmátrixon kívül esnek. \n", "# Ezek összekötésével a kép szélei közötti egyeneseket kapunk.\n", "\n", "import numpy as np\n", "import cv2\n", "\n", "src = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_GRAYSCALE)\n", "dst = cv2.Canny(src, 50, 200, None, 3)\n", "cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)\n", "\n", "lines = cv2.HoughLines(dst, 1, np.pi / 180, 300, None, 0, 0)\n", "\n", "sizemax = cdst.shape[0]\n", "\n", "if cdst.shape[1] > sizemax:\n", " sizemax = cdst.shape[1]\n", " \n", "sizemax = sizemax * 2\n", "\n", "if lines is not None:\n", " print('Detektált egyenesek száma:', len(lines))\n", " for i in range(0, len(lines)):\n", " rho = lines[i][0][0]\n", " theta = lines[i][0][1]\n", " a = math.cos(theta)\n", " b = math.sin(theta)\n", " x0 = a * rho\n", " y0 = b * rho\n", " \n", " # Computing line endpoints outside of image matrix\n", " pt1 = (int(x0 + sizemax * (-b)), int(y0 + sizemax * a))\n", " pt2 = (int(x0 - sizemax * (-b)), int(y0 - sizemax * a)) \n", " cv2.line(cdst, pt1, pt2, (0, 0, 255), 3, cv2.LINE_AA)\n", " \n", "cv2.imshow(\"Source\", src)\n", "\n", "cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", cdst)\n", "\n", "\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyWindow('image')" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "KB6kw6pR6U-M", "colab_type": "code", "colab": {} }, "source": [ "import numpy as np\n", "import cv2\n", "\n", "src = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_GRAYSCALE)\n", "dst = cv2.Canny(src, 50, 200, None, 3)\n", "cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)\n", "\n", "linesP = cv2.HoughLinesP(dst, 1, np.pi / 180, 50, None, 50, 10)\n", "if linesP is not None:\n", " for i in range(0, len(linesP)):\n", " l = linesP[i][0]\n", " cv2.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0, 0, 255), 3, cv2.LINE_AA)\n", "cv2.imshow(\"Source\", src)\n", "cv2.imshow(\"Detected Lines (in red) - Probabilistic Line Transform\", cdstP)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "wqRJ1Hac6U-P", "colab_type": "code", "colab": {} }, "source": [ "## kőrdetektálás\n", "\n", "import numpy as np\n", "import cv2\n", "\n", "src = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_COLOR)\n", "gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)\n", "gray = cv2.medianBlur(gray, 5)\n", "\n", "rows = gray.shape[0]\n", "circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, rows / 16,\n", " param1=200, param2=20,\n", " minRadius=10, maxRadius=60)\n", "\n", "if circles is not None:\n", " circles = np.uint16(np.around(circles))\n", " for i in circles[0, :]:\n", " center = (i[0], i[1])\n", " print(center, i[2])\n", " # circle center\n", " cv2.circle(src, center, 1, (255, 0, 0), 3)\n", " # circle outline\n", " radius = i[2]\n", " cv2.circle(src, center, radius, (0, 0, 255), 3)\n", "\n", "cv2.imshow(\"detected circles\", src)" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "cKSvsYsF6U-S", "colab_type": "code", "colab": {} }, "source": [ "## kontúrkeresést más paraméterezéssel, és más bementi képeken is!\n", "\n", "import cv2\n", "\n", "im = cv2.imread('gyava_oroszlan.jpg')\n", "imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)\n", "ret, thresh = cv2.threshold(imgray, 127, 255, 0)\n", "contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)\n", "\n", "print('Hierarchia:')\n", "print(hierarchy)\n", "\n", "print('Kontúrok száma:', len(contours))\n", "for cntrIdx in range(0, len(contours)):\n", " print(contours[cntrIdx].shape)\n", "\n", "cv2.drawContours(im, contours, -1, (0, 255, 0), 3)\n", "cv2.imshow('Contours', im)\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "lv6kUE_G6U-U", "colab_type": "code", "colab": {} }, "source": [ "# Kép betöltése és megjelenítése adott nevű ablakban. Billentyű lenyomás után az ablak eltűnik.\n", "\n", "img = cv2.imread('gyava_oroszlan.jpg')\n", "\n", "cv2.imshow('imshow', img)\n", "cv2.waitKey(0)\n", "cv2.destroyWindow('imshow')\n", "\n", "#Megjeleníthetünk egy üres ablakot is, amelyben majd később jelenítünk meg tartalmat.\n", "\n", "cv2.namedWindow('namedWindow')\n", "cv2.waitKey(0)\n", "cv2.imshow('namedWindow', img)\n", "\n", "#Megváltoztatjuk az ablak fejléc szövegét és új helyre mozgatjuk.\n", "\n", "cv2.setWindowTitle('namedWindow', 'namedWindow title')\n", "cv2.moveWindow('namedWindow', 200, 300)\n", "\n", "#Téglalap alakú terület kijelölése és eredményének konzolra írása.\n", "\n", "print('Válassz célterületet! Bal egérgomb + mozgatás, majd SPACE vagy ENTER. Megszakítás a c billentyűvel!')\n", "cv2.setWindowTitle('namedWindow', 'Valassz celteruletet!')\n", "roi = cv2.selectROI('namedWindow', img)\n", "print(roi)\n", "\n", "\n", "#Minden ablak bezárása.\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "YV9VBIwy6U-W", "colab_type": "code", "colab": {} }, "source": [ "# Az összetettebb példaprogramunk egy képet tölt be, és készít róla másolatot. \n", "# A bal egérgomb lenyomásakori koordinátát feljegyezzük, ez lesz a vonalunk kezdőpontja. \n", "# Lenyomott bal egérgomb melletti egér mozgás a vonal végpontját adja. \n", "# A vonalat be is rajzoljuk a képmátrixba és frissítjük a kijelzőt. \n", "# A bal egégomb felengedésekor befejezzük a vonal rajzolását. \n", "# A vörös színű programsorok mutatják az egérkezeléssel kapcsolatos utasításokat.\n", "\n", "\n", "import cv2\n", "\n", "start_x = start_y = -1\n", "button_down = False\n", "\n", "\n", "def mouse_event(event, x, y, flags, param):\n", " global start_x, start_y, button_down, im\n", "\n", " print('x, y:', x, y)\n", "\n", " if event == cv2.EVENT_LBUTTONDOWN:\n", " start_x = x\n", " start_y = y\n", " button_down = True\n", "\n", " if event == cv2.EVENT_MOUSEMOVE:\n", " if button_down:\n", " im = im_orig.copy()\n", " cv2.line(im, (start_x, start_y), (x, y), (0, 0, 255), 3)\n", " cv2.imshow('image', im)\n", "\n", " if event == cv2.EVENT_LBUTTONUP:\n", " start_x = start_y = -1\n", " button_down = False\n", "\n", "\n", "im = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_COLOR)\n", "im_orig = im.copy()\n", "cv2.imshow('image', im)\n", "\n", "cv2.setMouseCallback('image', mouse_event)\n", "\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "GM1FOk1_6U-Y", "colab_type": "code", "colab": {} }, "source": [ "# A példaprogram két csúszkás paraméterbeállítót helyez el az ablak felső részében. \n", "# Mindkettő önálló kezelőfüggvényt kap. A második esetben azt mutatjuk be, \n", "# hogyan lehet az OpenCV csúszka erős megszorításai mellett [-0.5, 0.5] közötti paraméterértékeket kapni,\n", "# 0.01 léptékkel. Ebben az esetben célszerű a felhasználót tájékoztatni a ténylegesen használt paraméter értékről, \n", "# például a konzolra írással!\n", "\n", "# Egy billentyű lenyomása után a param2 csúszka minimális, maximális és aktuális értékei programozottan megváltoznak. \n", "# Ez látható a csúszka beosztásán is.\n", "\n", "import numpy as np\n", "import cv2\n", "\n", "\n", "def onParam1Trackbar(x):\n", " print(\"=============================\")\n", " print('param1 csúszka pozíció:', x)\n", "\n", "\n", "def onParam2Trackbar(x):\n", " print(\"=============================\")\n", " print('param2 csúszka pozíció:', x)\n", " param = (x - 50) / 100\n", " print('Átalakított paraméter érték: {}'.format(param) )\n", "\n", "\n", "im = np.ndarray((200, 640, 3), np.uint8)\n", "im.fill(192)\n", "cv2.imshow('window', im)\n", "\n", "cv2.createTrackbar('param1', 'window', 5, 10, onParam1Trackbar)\n", "cv2.createTrackbar('param2', 'window', 50, 100, onParam2Trackbar)\n", "cv2.waitKey(0)\n", "cv2.setTrackbarMin('param2', 'window', 20)\n", "cv2.setTrackbarMax('param2', 'window', 50)\n", "cv2.setTrackbarPos('param2', 'window', 30)\n", "pos = cv2.getTrackbarPos('param2', 'window')\n", "\n", "print('Trackbar pozíció:', pos)\n", "\n", "cv2.waitKey(0)\n", "\n", "cv2.destroyAllWindows()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "oTRlKNIW6U-a", "colab_type": "code", "colab": {} }, "source": [ "# A keresőtáblát grafikonon is ábrázolhatjuk. \n", "# Ehhez könnyen használható eszközt biztosít a MatPlotLib csomag pyplot eszköze.\n", "# A példaprogram vörössel jelölt részei jelzik a szükséges hívásokat.\n", "\n", "import cv2\n", "import numpy as np\n", "from matplotlib import pyplot as plt\n", "\n", "# Grafikon beállítások\n", "fig = plt.figure(figsize=(4, 4), dpi=100)\n", "ax = fig.add_subplot(111)\n", "ax.set_title('Inverzió LUT diagram')\n", "fig.canvas.set_window_title('Inverzió LUT diagram')\n", "plt.xlabel('Eredeti intenzitásérték')\n", "plt.ylabel('Pont operáció eredménye')\n", "plt.xlim([0, 255])\n", "plt.ylim([0, 255])\n", "\n", "im = cv2.imread('gyava_oroszlan.jpg', cv2.IMREAD_GRAYSCALE)\n", "cv2.imshow('Eredeti', im)\n", "\n", "# Alappontok [0, 255] között, 8 bites, előjel nélküli egész számként\n", "x = np.arange(0, 256, 1, np.uint8)\n", "\n", "# Inverz készítés keresőtáblával\n", "lut = np.arange(0, 256, 1, np.uint8)\n", "lut = 255 - lut\n", "im_inv = cv2.LUT(im, lut, None)\n", "cv2.imshow('LUT', im_inv)\n", "\n", "# Diagram rajzolás\n", "plt.plot(x, x, 'g--', label='Eredeti')\n", "plt.plot(x, lut, 'r-', label='Inverzió')\n", "plt.legend()\n", "# plt.savefig('03_01_c_inverse_lut_diagram.png', bbox_inches='tight')\n", "plt.show()" ], "execution_count": 0, "outputs": [] }, { "cell_type": "code", "metadata": { "id": "BDLErt016U-d", "colab_type": "code", "colab": {} }, "source": [ "" ], "execution_count": 0, "outputs": [] } ] }