s470623-wko/wko-01.ipynb

1755 lines
270 KiB
Plaintext
Raw Permalink Normal View History

2022-11-27 23:20:17 +01:00
{
"cells": [
{
"cell_type": "markdown",
"id": "dfb11bac",
"metadata": {
"id": "dfb11bac"
},
"source": [
"![Logo 1](img/aitech-logotyp-1.jpg)\n",
"<div class=\"alert alert-block alert-info\">\n",
"<h1> Widzenie komputerowe </h1>\n",
"<h2> 01. <i>Wprowadzenie do widzenia komputerowego</i> [laboratoria]</h2> \n",
"<h3>Andrzej Wójtowicz (2021)</h3>\n",
"</div>\n",
"\n",
"![Logo 2](img/aitech-logotyp-2.jpg)"
]
},
{
"cell_type": "markdown",
"id": "7c64b51d",
"metadata": {
"id": "7c64b51d"
},
"source": [
"# Biblioteka OpenCV\n",
"\n",
"Podczas zajęć będziemy poruszali zagadnienia związane z [widzeniem komputerowym](https://en.wikipedia.org/wiki/Computer_vision) (ang. *computer vision*, CV). Tę tematykę możemy traktować jako rozwinięcie, czy też bardziej zaawansowaną formę [prztwarzania obrazów](https://en.wikipedia.org/wiki/Digital_image_processing) (ang. *image processing*, IP), gdzie tym razem będziemy starali się wyciągnąć pewną bardziej zaawansowaną wiedzę płynącą z obrazów statycznych lub wideo (cf. dyskusja na [Artificial Intelligence Stack Exchange](https://ai.stackexchange.com/a/13588)). Przedmiot ma formę laboratoryjną, zatem główną dyskusję dotyczącą zakresu obu dziedzin zostawimy w ramach dodatkowej literatury uzupełniającej.\n",
"\n",
"Standardem dla algorytmów z dziedzin IP/CV jest biblioteka [OpenCV](https://opencv.org/), która implementuje wiele z tych algorytmów oraz jest aktywnie rozwijana przez społeczność. Sama biblioteka posiada interfejsy do wielu języków programowania, natomiast my skupimy się na języku [Python](https://www.python.org/), który będzie dla nas idealny na potrzeby intensywnego prototypowania. Dokumentację online będzie głównie prowadziła do języka C++, ponieważ nie ma dedykowanej online dla Pythona, ale argumenty funkcji i metod są analogiczne.\n",
"\n",
"Początkowe zajęcia będą głównie dotyczyły zagadnień IP, tak aby zapoznać się z biblioteką OpenCV, a dalsze zajęcia będą już związane z CV.\n",
"\n",
"## Instalacja\n",
"\n",
"Materiały do zajęć Jupyter Notebook są tworzone na serwerze JupyterHub z kernelem Python 3. Pominiemy tutaj tworzenie wirtualnego środowiska, jednak należy mieć na uwadze, że poniższe polecenia mogą być też przydatne podczas próby uruchomienia notebooków lub programów na własnym komputerze.\n",
"\n",
"Poniższe polecenie wyświetla używaną wersję Pythona:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "8fda1098",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 756,
"status": "ok",
"timestamp": 1666007608272,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "8fda1098",
"outputId": "2549a835-4457-401c-c5ce-1d2d4f0186b2"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3.7.14\n"
]
}
],
"source": [
"import platform\n",
"print(platform.python_version())"
]
},
{
"cell_type": "markdown",
"id": "c8e7b1e3",
"metadata": {
"id": "c8e7b1e3"
},
"source": [
"Niezbędne moduły zainstalujemy poprzez menadżer `pip`. Sama biblioteka OpenCV, abstrahując od np. paczek debianowych, posiada [4 możliwe opcje instalacji](https://pypi.org/project/opencv-contrib-python/). My zainstalujemy pełną wersję tej biblioteki, a dodatkowo doinstalujemy pakiety związane m.in. wyświetlaniem grafiki oraz uczeniem maszynowym."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "78e42f58",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 14652,
"status": "ok",
"timestamp": 1666007622922,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "78e42f58",
"outputId": "b533f0c4-44f7-4792-ff81-d94ec30e483c"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n",
"Collecting opencv-contrib-python==4.5.3.56\n",
" Downloading opencv_contrib_python-4.5.3.56-cp37-cp37m-manylinux2014_x86_64.whl (56.1 MB)\n",
"\u001b[K |████████████████████████████████| 56.1 MB 1.2 MB/s \n",
"\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (1.21.6)\n",
"Requirement already satisfied: scipy in /usr/local/lib/python3.7/dist-packages (1.7.3)\n",
"Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (3.2.2)\n",
"Requirement already satisfied: scikit-learn in /usr/local/lib/python3.7/dist-packages (1.0.2)\n",
"Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (1.4.4)\n",
"Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (0.11.0)\n",
"Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (2.8.2)\n",
"Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib) (3.0.9)\n",
"Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from kiwisolver>=1.0.1->matplotlib) (4.1.1)\n",
"Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.1->matplotlib) (1.15.0)\n",
"Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn) (3.1.0)\n",
"Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn) (1.2.0)\n",
"Installing collected packages: opencv-contrib-python\n",
"Successfully installed opencv-contrib-python-4.5.3.56\n"
]
}
],
"source": [
"!pip3 install --user --disable-pip-version-check opencv-contrib-python==4.5.3.56 numpy scipy matplotlib scikit-learn"
]
},
{
"cell_type": "markdown",
"id": "1d241431",
"metadata": {
"id": "1d241431"
},
"source": [
"Sama biblioteka posiada też własne moduły związane z wyświetlaniem grafiki (moduł *HighGUI*) oraz z uczeniem maszynowym (moduł *ml*), jednak my raczej nie będziemy z nich korzystać podczas zajęć (aczkolwiek mogą być przydatne podczas realizacji projektu).\n",
"\n",
"Wszystkie moduły, algorytmy i zmienne są dostępne z poziomu modułu `cv2`:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "8eca2db5",
"metadata": {
"executionInfo": {
"elapsed": 406,
"status": "ok",
"timestamp": 1666007623321,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "8eca2db5"
},
"outputs": [],
"source": [
"import cv2 as cv"
]
},
{
"cell_type": "markdown",
"id": "82701dba",
"metadata": {
"id": "82701dba"
},
"source": [
"Poniższym poleceniem możemy również sprawdzić wersję zainstalowanej biblioteki:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "f34f551a",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 7,
"status": "ok",
"timestamp": 1666007623322,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "f34f551a",
"outputId": "1aa86a57-7041-4e80-9be2-3543df45bb7f"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"4.6.0\n"
]
}
],
"source": [
"print(cv.__version__)"
]
},
{
"cell_type": "markdown",
"id": "7a39875c",
"metadata": {
"id": "7a39875c"
},
"source": [
"## Obrazy jako tablice\n",
"\n",
"Obraz możemy traktować jako standardową tablicę [NumPy](https://www.numpy.org/) zawierającą dane dotyczące danych pikseli. Im większa liczba pikseli w obrazie, tym większa jest jego rozdzielczość. Intuicyjnie można przyjąć, że piksele są niewielkimi blokami informacji ułożonymi w postaci siatki dwuwymiarowej, a głębokość piksela odnosi się do informacji o kolorze. Aby obraz mógł być przetworzony przez komputer, to taki obraz musi zostać przekonwertowany na postać binarną. Kolor obrazu można obliczyć w następujący sposób:\n",
"\n",
"Liczba kolorów (odcieni) = 2<sup>*bpp*</sup>, gdzie *bpp* oznacza bity na piksel.\n",
"\n",
"Większa liczba bitów na piksel daje więcej możliwych kolorów na obrazach.\n",
"\n",
"| Bity na piksel | Liczba kolorów |\n",
"| :------------: | :------------: |\n",
"| 1 | 2<sup>1</sup> = 2 |\n",
"| 2 | 2<sup>2</sup> = 4 |\n",
"| 3 | 2<sup>3</sup> = 8 |\n",
"| 4 | 2<sup>4</sup> = 16 |\n",
"| 8 | 2<sup>8</sup> = 256 |\n",
"| 16 | 2<sup>16</sup> = 65536 |\n",
"\n",
"Spójrzmy na trzy typowe reprezentacje obrazów.\n",
"\n",
"### Obraz czarno-biały\n",
"\n",
"Obraz binarny składa się z 1 bita na piksel, a więc może mieć tylko dwa możliwe kolory, tj. czarny lub biały. Kolor czarny jest reprezentowany przez wartość 0, a 1 oznacza biel (czasem w użyciu są reprezentacje, które mają odwrotne wartości czerni i bieli).\n",
"\n",
"![Obraz czarno-biały jako tablica bitów](img/binary-image.png)\n",
"\n",
"### Obraz w skali odcieni szarości\n",
"\n",
"Obraz w skali szarości składa się z 8 bitów na piksel. Oznacza to, że może mieć 256 różnych odcieni, przy czym 0 pikseli będzie reprezentować kolor czarny, a 255 oznacza biel.\n",
"\n",
"![Przykładowy obraz w skali odcieni szarości](img/lena-grayscale.png)\n",
"\n",
"### Obraz kolorowy\n",
"\n",
"Kolorowe obrazy w standardowej formie są reprezentowane jako połączenie barwy czerwonej, niebieskiej i zielonej - wszystkie inne kolory można uzyskać, mieszając te podstawowe kolory we właściwych proporcjach.\n",
"\n",
"![Składowe RGB](img/rgb-colors.png)\n",
"\n",
"Kolorowy obraz składa się również z 8 bitów na piksel, z tym że na taki obraz składają się 3 kanały (czerwony, zielony i niebieski). W rezultacie 256 różnych natężeń danego koloru podstawowego można przedstawić za pomocą 0 oznaczającego najmniej intensywny i 255 najbardziej intensywny. Dla poniższego obrazu pawiana:\n",
"\n",
"![Obraz pawiana](img/baboon.png)\n",
"\n",
"podstawowe parametry dotyczące wymiarów prezentują się następująco:\n",
"\n",
"```plaintext\n",
"Shape\n",
"(288, 289, 3)\n",
"288: Pixel height (wysokość w pikselach)\n",
"289: Pixel width (szerokość w pikselach)\n",
"3: color channel (liczba kanałów)\n",
"```\n",
"\n",
"Taki obraz możemy reprezentować w postaci trójwymiarowej tablicy:\n",
"\n",
"![Kanały RGB obrazu pawiana](img/baboon-3d.png)"
]
},
{
"cell_type": "markdown",
"id": "650cae81",
"metadata": {
"id": "650cae81"
},
"source": [
"## Wczytywanie obrazów\n",
"\n",
"Przy użyciu funkcji [`cv.imread()`](https://docs.opencv.org/4.5.3/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56) możemy odczytać obraz. Pierwszy argument to lokalizacja pliku. Obraz powinien znajdować się w katalogu roboczym lub powinna zostać podana ścieżka bezwzględna do pliku.\n",
"\n",
"Drugi argument (opcjonalny) jest flagą oznaczającą w jaki sposób obraz powinien zostać wczytany:\n",
"\n",
"* [`cv.IMREAD_COLOR`](https://docs.opencv.org/4.5.3/d4/da8/group__imgcodecs.html#gga61d9b0126a3e57d9277ac48327799c80af660544735200cbe942eea09232eb822) - wczytuje kolorowy obraz z pominięciem przezroczystości; flaga domyślna,\n",
"* [`cv.IMREAD_GRAYSCALE`](https://docs.opencv.org/4.5.3/d4/da8/group__imgcodecs.html#gga61d9b0126a3e57d9277ac48327799c80ae29981cfc153d3b0cef5c0daeedd2125) : wczytuje obraz w skali odcieni szarości,\n",
"* [`cv.IMREAD_UNCHANGED`](https://docs.opencv.org/4.5.3/d4/da8/group__imgcodecs.html#gga61d9b0126a3e57d9277ac48327799c80aeddd67043ed0df14f9d9a4e66d2b0708) : wczytuje obraz razem z kanałem alfa (przezroczystość).\n",
"\n",
"Zamiast trzech powyższych flag można alternatywnie przekazać odpowiednio 1, 0 lub -1."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "d7fccc7d",
"metadata": {
"executionInfo": {
"elapsed": 616,
"status": "ok",
"timestamp": 1666007894727,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "d7fccc7d"
},
"outputs": [],
"source": [
"image = cv.imread(\"img/baboon.png\", cv.IMREAD_COLOR)"
]
},
{
"cell_type": "markdown",
"id": "d9024a2d",
"metadata": {
"id": "d9024a2d"
},
"source": [
"Sprawdźmy typ zmiennej:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "78c727fe",
"metadata": {
"id": "78c727fe"
},
"outputs": [],
"source": [
"print(type(image))"
]
},
{
"cell_type": "markdown",
"id": "7c488a2d",
"metadata": {
"id": "7c488a2d"
},
"source": [
"Sprawdźmy kształt tablicy:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "67bafe94",
"metadata": {
"id": "67bafe94"
},
"outputs": [],
"source": [
"print(image.shape)"
]
},
{
"cell_type": "markdown",
"id": "68d3db9e",
"metadata": {
"id": "68d3db9e"
},
"source": [
"[Typ danych](https://numpy.org/doc/stable/user/basics.types.html) tablicy:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8463a5df",
"metadata": {
"id": "8463a5df"
},
"outputs": [],
"source": [
"print(image.dtype)"
]
},
{
"cell_type": "markdown",
"id": "243e1fd8",
"metadata": {
"id": "243e1fd8"
},
"source": [
"Jak widać, poszczególne piksele na kanałach są 8-bitowymi liczbami całkowitymi bez znaku."
]
},
{
"cell_type": "markdown",
"id": "185a63ad",
"metadata": {
"id": "185a63ad"
},
"source": [
"## Wyświetlanie obrazów przy pomocy Matplotlib\n",
"\n",
"W notebooku Jupyter najwygodniej wyświetla się obrazy przy pomocy biblioteki [Matplotlib](https://matplotlib.org/), a dokładniej modułu `pyplot`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c4516873",
"metadata": {
"id": "c4516873"
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import matplotlib\n",
"%matplotlib inline"
]
},
{
"cell_type": "markdown",
"id": "7e320379",
"metadata": {
"id": "7e320379"
},
"source": [
"Przed wyświetleniem obrazu musimy dokonać drobnej konwersji obrazu. OpenCV domyślnie wczytuje obraz w formacie BGR, natomiast Matplotlib pracuje na obrazie w formacie RGB. Do konwersji użyjemy funkcji [`cv.cvtColor()`](https://docs.opencv.org/4.5.3/d8/d01/group__imgproc__color__conversions.html#ga397ae87e1288a81d2363b61574eb8cab), której pierwszym argumentem jest konwertowany obraz, a drugim sposób konwersji (w tym wypadku definiowany przez stałą [`cv.COLOR_BGR2RGB`](https://docs.opencv.org/4.5.3/d8/d01/group__imgproc__color__conversions.html#gga4e0972be5de079fed4e3a10e24ef5ef0ad3db9ff253b87d02efe4887b2f5d77ee)). *Nota bene*, czasami lepiej jest operować w przestrzeni barw [HSV](https://pl.wikipedia.org/wiki/HSV_(grafika)) niż RGB."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bd083db2",
"metadata": {
"id": "bd083db2"
},
"outputs": [],
"source": [
"image2 = cv.cvtColor(image, cv.COLOR_BGR2RGB)"
]
},
{
"cell_type": "markdown",
"id": "f8b4ae77",
"metadata": {
"id": "f8b4ae77"
},
"source": [
"Po przekonwertowaniu obrazu możemy wyświetlić go przy pomocy `pyplot`. Użyjemy do tego funkcji [`matplotlib.pyplot.imshow()`](https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.imshow):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e39ea012",
"metadata": {
"id": "e39ea012"
},
"outputs": [],
"source": [
"plt.imshow(image2)"
]
},
{
"cell_type": "markdown",
"id": "fc1c34db",
"metadata": {
"id": "fc1c34db"
},
"source": [
"Zwróćmy uwagę, że piksel o współrzędnych `(0, 0)` znajduje się w lewym górnym rogu.\n",
"\n",
"Można też obejść się bez konwersji i po prostu odwrócić w locie kanały w tablicy `numpy`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aa374723",
"metadata": {
"id": "aa374723"
},
"outputs": [],
"source": [
"plt.imshow(image[..., ::-1])"
]
},
{
"cell_type": "markdown",
"id": "232c3da7",
"metadata": {
"id": "232c3da7"
},
"source": [
"Sprawdźmy kolejny plik:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bccd478c",
"metadata": {
"id": "bccd478c"
},
"outputs": [],
"source": [
"x_small = cv.imread(\"img/x_small.jpg\", cv.IMREAD_GRAYSCALE)\n",
"plt.imshow(x_small, cmap = 'gray');"
]
},
{
"cell_type": "markdown",
"id": "2ba47521",
"metadata": {
"id": "2ba47521"
},
"source": [
"Wczytaliśmy obraz w skali odcieni szarości. Podczas wyświetlania obrazu możemy ustawić mapę kolorów (parametr `cmap`). Dodatkowo, jeśli na końcu polecenia damy średnik (`;`), to w wynikowej komórce notebooka nie będzie się wyświetlał zwracany typ.\n",
"\n",
"Czasami warto również wyświetlić pasek z informacją o tym jaka wartość odpowiada za dany odcień:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "066b4778",
"metadata": {
"id": "066b4778"
},
"outputs": [],
"source": [
"plt.imshow(x_small, cmap = 'gray');\n",
"plt.colorbar();"
]
},
{
"cell_type": "markdown",
"id": "c561f1ea",
"metadata": {
"id": "c561f1ea"
},
"source": [
"## Proste operacje\n",
"\n",
"Możemy sprawdzić, że obraz jest w istocie tablicą NumPy o wartościach w zakresie od 0 do 255:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fd384995",
"metadata": {
"id": "fd384995"
},
"outputs": [],
"source": [
"print(x_small)"
]
},
{
"cell_type": "markdown",
"id": "794d3cb6",
"metadata": {
"id": "794d3cb6"
},
"source": [
"Wracając do samej biblioteki Matplotlib, możemy również np. sterować rozmiarem wyświetlanego obrazu oraz nadać mu tytuł:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5998b09d",
"metadata": {
"id": "5998b09d"
},
"outputs": [],
"source": [
"plt.figure(figsize = [6, 6])\n",
"plt.imshow(x_small, cmap = 'gray')\n",
"plt.title('Small X', fontsize = 20);"
]
},
{
"cell_type": "markdown",
"id": "059710ee",
"metadata": {
"id": "059710ee"
},
"source": [
"Niektóre parametry Matlplotlib (np. `figsize`) możemy ustawić na domyślne przy pomocy [`matplotlib.rcParams`](https://matplotlib.org/stable/tutorials/introductory/customizing.html#matplotlib-rcparams).\n",
"\n",
"Podczas wczytywania obrazów zawierających przezroczystość należy zwrócić uwagę na sposób wczytywania obrazu:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "77861dde",
"metadata": {
"id": "77861dde"
},
"outputs": [],
"source": [
"img_lfl = cv.imread(\"img/linux_foundation.png\", cv.IMREAD_COLOR)\n",
"plt.imshow(img_lfl);"
]
},
{
"cell_type": "markdown",
"id": "46deb78b",
"metadata": {
"id": "46deb78b"
},
"source": [
"Mamy tutaj obraz 4-kanałowy, przez co musimy go wczytać jako `cv.IMREAD_UNCHANGED`, no i dodatkowo odpowiednio go przekonwertować:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ce0a9061",
"metadata": {
"id": "ce0a9061"
},
"outputs": [],
"source": [
"img_lfl = cv.imread(\"img/linux_foundation.png\", cv.IMREAD_UNCHANGED)\n",
"plt.imshow(cv.cvtColor(img_lfl, cv.COLOR_BGRA2RGBA));"
]
},
{
"cell_type": "markdown",
"id": "9f0f1db3",
"metadata": {
"id": "9f0f1db3"
},
"source": [
"Możemy również podzielić obraz na poszczególne kanały i ew. ponownie je połączyć, odpowiednio przy pomocy funkcji [`cv.split()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#ga0547c7fed86152d7e9d0096029c8518a) i [`cv.merge()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#ga7d7b4d6c6ee504b30a20b1680029c7b4):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "60ab993b",
"metadata": {
"id": "60ab993b"
},
"outputs": [],
"source": [
"b, g, r = cv.split(image)\n",
"plt.imshow(b, cmap = 'gray')\n",
"plt.title('Blue channel', fontsize = 20);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f8418716",
"metadata": {
"id": "f8418716"
},
"outputs": [],
"source": [
"restored_image = cv.merge((r,g,b))\n",
"plt.imshow(restored_image);"
]
},
{
"cell_type": "markdown",
"id": "9ba6f17e",
"metadata": {
"id": "9ba6f17e"
},
"source": [
"Dostęp do poszczególnych pikseli odbywa się tak jak w macierzy, czyli podając wiersz i kolumnę. Poniżej możemy zobaczyć zmianę piksela na współrzędnych `(1, 1)`:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7a018550",
"metadata": {
"id": "7a018550"
},
"outputs": [],
"source": [
"plt.imshow(x_small, cmap = 'gray');"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3aebbb19",
"metadata": {
"id": "3aebbb19"
},
"outputs": [],
"source": [
"x_small[1, 1] = 255\n",
"plt.imshow(x_small, cmap = 'gray');"
]
},
{
"cell_type": "markdown",
"id": "14278fd3",
"metadata": {
"id": "14278fd3"
},
"source": [
"Przycinanie odbywa się przez znany w Pythonie tzw. *slicing*, czyli wycinki:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "52b49c59",
"metadata": {
"id": "52b49c59"
},
"outputs": [],
"source": [
"plt.imshow(x_small[3:8, 2:], cmap = 'gray');"
]
},
{
"cell_type": "markdown",
"id": "e191d0e8",
"metadata": {
"id": "e191d0e8"
},
"source": [
"Możemy również ustawić kolor dla kilku kanałów jednocześnie dla zadanego regionu. Wykonamy to na kopii obrazu:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "94db5076",
"metadata": {
"id": "94db5076"
},
"outputs": [],
"source": [
"image_edited = image.copy()\n",
"image_edited[100:200, 150:250] = (128, 0, 128)\n",
"plt.imshow(image_edited[..., ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "f340e7ec",
"metadata": {
"id": "f340e7ec"
},
"source": [
"Przeskalowanie obrazu odbywa się przez [`cv.resize()`](https://docs.opencv.org/4.5.3/da/d54/group__imgproc__transform.html#ga47a974309e9102f5f08231edc7e7529d), w której albo podajemy dokładne docelowe wymiary, albo podajemy współczynniki skalowania na osiach *x* i *y*; możemy też uwzględnić odpowiednią metodę interpolacji. Matplotlib nie wyświetli nam wprost powiększonych obrazków, ale będziemy mogli zauważyć zmianę poprzez np. poprzez zmianę zakresów skali osi *x* i *y*:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fda0546a",
"metadata": {
"id": "fda0546a"
},
"outputs": [],
"source": [
"x_small_resized_1 = cv.resize(x_small, (40, 40))\n",
"plt.imshow(x_small_resized_1, cmap='gray');"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e85d632e",
"metadata": {
"id": "e85d632e"
},
"outputs": [],
"source": [
"x_small_resized_2 = cv.resize(x_small, None, fx=3, fy=4, interpolation=cv.INTER_CUBIC)\n",
"plt.imshow(x_small_resized_2, cmap='gray');"
]
},
{
"cell_type": "markdown",
"id": "09a01036",
"metadata": {
"id": "09a01036"
},
"source": [
"Obrót obrazu dokonywany jest przez funkcję [`cv.flip()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#gaca7be533e3dac7feb70fc60635adf441), w której podajemy według której osi ma nastąpić obrót (`0`: *x*, `1`: *y*, `-1`: obie)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fd673085",
"metadata": {
"id": "fd673085"
},
"outputs": [],
"source": [
"image_flipped = cv.flip(image, 0)\n",
"plt.imshow(image_flipped[..., ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "e4c0f0d8",
"metadata": {
"id": "e4c0f0d8"
},
"source": [
"Może pojawić się potrzeba utworzenia nowych obrazów, więc najczęściej używa się do tego biblioteki NumPy:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc86f294",
"metadata": {
"id": "fc86f294"
},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "markdown",
"id": "bc67f439",
"metadata": {
"id": "bc67f439"
},
"source": [
"Poniższe wywołania pokazują kilka wariantów. Możemy utworzyć pustą trójwymiarową macierz wypełnioną zerami przy pomocy [`numpy.zeros()`](https://numpy.org/doc/stable/reference/generated/numpy.zeros.html):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a8cb1c58",
"metadata": {
"id": "a8cb1c58"
},
"outputs": [],
"source": [
"image_empty = np.zeros((50, 100, 3), dtype='uint8')\n",
"plt.imshow(image_empty);"
]
},
{
"cell_type": "markdown",
"id": "dde3f2b0",
"metadata": {
"id": "dde3f2b0"
},
"source": [
"Jeżeli chcemy ustawić jakąś początkową wartość, to możemy przeskalować tablicę jedynek uzyskaną z [`numpy.ones()`](https://numpy.org/doc/stable/reference/generated/numpy.ones.html):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7190f1df",
"metadata": {
"id": "7190f1df"
},
"outputs": [],
"source": [
"image_empty = 128*np.ones((50, 100, 3), dtype='uint8')\n",
"plt.imshow(image_empty)"
]
},
{
"cell_type": "markdown",
"id": "4f785c86",
"metadata": {
"id": "4f785c86"
},
"source": [
"Jeżeli nowy obraz powinien mieć takie same wymiary jak inny obraz, to możemy do tej operacji użyć [`numpy.ones_like()`](https://numpy.org/doc/stable/reference/generated/numpy.ones_like.html):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0f99dda7",
"metadata": {
"id": "0f99dda7"
},
"outputs": [],
"source": [
"empty_from_other = 255*np.ones_like(image)\n",
"plt.imshow(empty_from_other);"
]
},
{
"cell_type": "markdown",
"id": "ac02a038",
"metadata": {
"id": "ac02a038"
},
"source": [
"Czasami może być przydatne utworzenie maski zawierającej informację o tym czy np. dany piksel zawiera wartość w danym zakresie. Przy pomocy [`cv.inRange()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#ga48af0ab51e36436c5d04340e036ce981) możemy sprawdzić zakresy dla trzech kanałów; przy okazji możemy zobaczyć jak zgrupować kilka obrazów jednocześnie (por. [`matplotlib.pyplot.subplot()`](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot.html)):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d536f6e6",
"metadata": {
"id": "d536f6e6"
},
"outputs": [],
"source": [
"mask = cv.inRange(image, (0,100,150), (100,150,200))\n",
"\n",
"plt.figure(figsize=[10,10])\n",
"plt.subplot(121)\n",
"plt.imshow(image[..., ::-1])\n",
"plt.title(\"Baboon\")\n",
"plt.subplot(122)\n",
"plt.imshow(mask, cmap='gray')\n",
"plt.title(\"Mask\");"
]
},
{
"cell_type": "markdown",
"id": "622a0f0d",
"metadata": {
"id": "622a0f0d"
},
"source": [
"Standardowo operujemy w zakresie liczb całkowitych `[0; 255]`, przez co jeśli w wyniku jakiejś operacji mielibyśmy wykroczyć poza zakres, to skończy się to albo operacją modulo albo np. uzyskaniem wartości ujemnych, co ostatecznie przełoży się na błędny wynik.\n",
"\n",
"Załóżmy, że chcemy podbić kontrast o 50%. Przemnożenie obrazu o wartość 1.5 zwróci nam tablicę o wartościach liczb rzeczywistych:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d48cf055",
"metadata": {
"id": "d48cf055"
},
"outputs": [],
"source": [
"new_image = image * 1.5\n",
"print(new_image.dtype)"
]
},
{
"cell_type": "markdown",
"id": "b2dbeacc",
"metadata": {
"id": "b2dbeacc"
},
"source": [
"W takim wypadku możemy naiwnie spróbować przekonwertować to ponownie do liczb całkowitych, ale wartości powyżej 255 zostaną obliczone według operacji modulo, przez co wynik będzie niezadowalający:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "411dabb9",
"metadata": {
"id": "411dabb9"
},
"outputs": [],
"source": [
"new_image = np.uint8(new_image)\n",
"plt.imshow(new_image[..., ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "1fea50c7",
"metadata": {
"id": "1fea50c7"
},
"source": [
"Rozwiązaniem jest przycięcie wartości przy użyciu [`numpy.clip()`](https://numpy.org/doc/stable/reference/generated/numpy.clip.html) do zakresu `[0; 255]` jeśli operujemy na liczbach całkowitych, z ew. znormalizowaniem wartości do zakresu `[0; 1]` jeśli chcemy pracować na liczbach rzeczywistych:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eac38489",
"metadata": {
"id": "eac38489"
},
"outputs": [],
"source": [
"new_image = image * 1.5\n",
"clipped_new_image = np.clip(new_image, 0, 255)\n",
"clipped_new_image = np.uint8(clipped_new_image)\n",
"plt.imshow(clipped_new_image[..., ::-1]);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bc2cb3df",
"metadata": {
"id": "bc2cb3df"
},
"outputs": [],
"source": [
"new_image = (image * 1.5)/255\n",
"clipped_new_image = np.clip(new_image, 0, 1)\n",
"plt.imshow(clipped_new_image[..., ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "a2f8c4a5",
"metadata": {
"id": "a2f8c4a5"
},
"source": [
"Jeśli zwiększymy jasność i potencjalnie wyjdziemy poza zakres `[0; 255]`, to ponownie wartości zostaną przekręcone:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ce823b52",
"metadata": {
"id": "ce823b52"
},
"outputs": [],
"source": [
"new_image = image + 50\n",
"plt.imshow(new_image[..., ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "4be595c9",
"metadata": {
"id": "4be595c9"
},
"source": [
"Z drugiej strony należy mieć też na uwadze pewne subtelne konwersje, które mogą odbywać się niejawnie. Poniżej mamy operację, która powoduje użycie danych typu `int16`, przez co następuje niejawna konwersja i przycięcie (jest o tym informacja w ostrzeżeniu):"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c93528a2",
"metadata": {
"id": "c93528a2"
},
"outputs": [],
"source": [
"new_image = image + (-50)\n",
"plt.imshow(new_image[..., ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "6b3a8f53",
"metadata": {
"id": "6b3a8f53"
},
"source": [
"Rozwiązaniem powyższych problemów może być w przypadku zmiany jasności użycie funkcji [`cv.add()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#ga10ac1bfb180e2cfda1701d06c24fdbd6) i [`cv.substract()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#gaa0f00d98b4b5edeaeb7b8333b2de353b), a w przypadku zmiany kontrastu użycie [`cv.multiply()`](https://docs.opencv.org/4.5.3/d2/de8/group__core__array.html#ga979d898a58d7f61c53003e162e7ad89f), konwersja do typu np. `float64` i powrót do `uint8` z przycięciem wartości."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fba240f9",
"metadata": {
"id": "fba240f9"
},
"outputs": [],
"source": [
"matrix = np.ones(image.shape, dtype='uint8') * 50\n",
"\n",
"image_brighter = cv.add(image, matrix)\n",
"plt.imshow(image_brighter[:, :, ::-1]);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "79b54cc1",
"metadata": {
"id": "79b54cc1"
},
"outputs": [],
"source": [
"image_darker = cv.subtract(image, matrix)\n",
"plt.imshow(image_darker[:, :, ::-1]);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2fe3e9d5",
"metadata": {
"id": "2fe3e9d5"
},
"outputs": [],
"source": [
"matrix = np.ones(image.shape, dtype = 'float64')\n",
"\n",
"image_lower = np.uint8(cv.multiply(np.float64(image), matrix, scale = 0.8))\n",
"plt.imshow(image_lower[:, :, ::-1]);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "65da736b",
"metadata": {
"id": "65da736b"
},
"outputs": [],
"source": [
"image_higher = np.uint8(np.clip(cv.multiply(np.float64(image), matrix, scale = 1.2) , 0, 255))\n",
"plt.imshow(image_higher[:, :, ::-1]);"
]
},
{
"cell_type": "markdown",
"id": "54e22dd0",
"metadata": {
"id": "54e22dd0"
},
"source": [
"## Zapisywanie obrazów\n",
"\n",
"Funkcja [`cv.imwrite()`](https://docs.opencv.org/4.5.3/d4/da8/group__imgcodecs.html#gabbc7ef1aa2edfaa87772f1202d67e0ce) zapisuje obraz do pliku. Pierwszy argument to nazwa pliku, drugi argument to obraz, który chcemy zapisać. Np. poniższe polecenie spowodowałoby zapisanie obrazu w formacie PNG w katalogu roboczym:\n",
"\n",
"```python\n",
"cv.imwrite('pawian.png', image)\n",
"```\n",
"\n",
"## Anotowanie\n",
"\n",
"Sprawdźmy w jaki sposób do pustego obrazu dodać kilka obiektów geometrycznych. Użyjemy do tego funkcji [`cv.line()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga7078a9fae8c7e7d13d24dac2520ae4a2), [`cv.circle()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#gaf10604b069374903dbd0f0488cb43670) , [`cv.rectangle()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga07d2f74cadcf8e305e810ce8eed13bc9), [`cv.ellipse()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga28b2267d35786f5f890ca167236cbc69), [`cv.polylines()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#gaa3c25f9fb764b6bef791bf034f6e26f5) i [`cv.putText()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga5126f47f883d730f633d74f07456c576). Funkcje te będą nadpisywały wejściowy obraz.\n",
"\n",
"W powyższych funkcjach pojawiają się wspólne argumenty:\n",
"\n",
"* `img` - obraz, na którym będziemy umieszczać obiekty,\n",
"* `color` - kolor obiektu podany w formacie jako krotka (*tuple*), np. `(255, 0, 0)`; w przypadku obrazów w skali odcieni szarości wystarczy podać wartość skalarną,\n",
"* `thickness` - grubość linii, okręgu, itp.; w przypadku wartości `-1` w przypadku zamkniętych figur jak okrąg, obiekt zostanie wypełniony wewnątrz wskazanym kolorem; domyślna wartość to `1`,\n",
"* `lineType` : rodzaj linii, np. 8-sąsiedztwo, antyaliasing, itp.; domyślnie jest 8-sąsiedztwo, natomiast wartość [`cv.LINE_AA`](https://docs.opencv.org/4.5.3/d0/de1/group__core.html#ggaf076ef45de481ac96e0ab3dc2c29a777a85fdabe5335c9e6656563dfd7c94fb4f) włącza antyaliasing.\n",
"\n",
"Na początku zaimportujemy bibliotekę NumPy i przy pomocy funkcji [`numpy.zeros()`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html) utworzymy pusty 3-warstwowy czarny obraz o rozmiarze 512x512."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "efd40bcf",
"metadata": {
"id": "efd40bcf"
},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"img = np.zeros((512,512,3), np.uint8)\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "2b23ae8a",
"metadata": {
"id": "2b23ae8a"
},
"source": [
"W dalszej części operowali na obrazie w formacie RGB.\n",
"\n",
"### Linie\n",
"\n",
"Aby narysować linię, musimy podać początkowe i końcowe współrzędne linii. Przy pomocy [`cv.line()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga7078a9fae8c7e7d13d24dac2520ae4a2) narysujemy na obrazie czerwoną linię od lewego górnego rogu do prawego dolnego rogu o grubości 5 pikseli."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eecb08e6",
"metadata": {
"id": "eecb08e6"
},
"outputs": [],
"source": [
"cv.line(img, (0,0), (511,511), (255,0,0), 5)\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "bb1a0101",
"metadata": {
"id": "bb1a0101"
},
"source": [
"### Prostokąty\n",
"\n",
"Aby narysować prostokąt, potrzebujemy lewego górnego rogu i prawego dolnego rogu prostokąta. Tym razem przy pomocy [`cv.rectangle()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga07d2f74cadcf8e305e810ce8eed13bc9) narysujemy zielony prostokąt w prawym górnym rogu obrazu."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9d3cc9ad",
"metadata": {
"id": "9d3cc9ad"
},
"outputs": [],
"source": [
"cv.rectangle(img, (384,0), (510,128), (0,255,0), 3)\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "cdcb4b70",
"metadata": {
"id": "cdcb4b70"
},
"source": [
"### Okręgi\n",
"\n",
"Aby narysować okrąg, potrzebujemy jego współrzędnych środka i promienia. Przy pomocy [`cv.circle()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#gaf10604b069374903dbd0f0488cb43670) narysujemy okrąg wewnątrz prostokąta narysowanego powyżej."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "48207862",
"metadata": {
"id": "48207862"
},
"outputs": [],
"source": [
"cv.circle(img, (447,63), 63, (0,0,255), -1)\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "60678ca6",
"metadata": {
"id": "60678ca6"
},
"source": [
"### Elipsy\n",
"\n",
"Aby narysować elipsę, musimy przekazać kilka argumentów. Jednym argumentem jest położenie środka `(x, y)`. Następnym argumentem jest długość dwóch osi (długość głównej osi i mniejsza długość osi). Kolejny parametr to kąt obrotu elipsy w kierunku przeciwnym do ruchu wskazówek zegara. Kolejne dwa argumenty oznaczają początek i koniec łuku elipsy mierzonego zgodnie z ruchem wskazówek zegara od głównej osi. tj. podanie wartości `0` i `360` daje pełną elipsę. Aby uzyskać więcej informacji, sprawdź dokumentację [`cv.ellipse()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga28b2267d35786f5f890ca167236cbc69). Poniższy kod rysuje pół elipsy na środku obrazu."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4423e4b0",
"metadata": {
"id": "4423e4b0"
},
"outputs": [],
"source": [
"cv.ellipse(img, (256,256), (100,50), 0, 0, 180, 255, -1)\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "1219997d",
"metadata": {
"id": "1219997d"
},
"source": [
"### Poligony\n",
"\n",
"Aby narysować wielokąt, najpierw potrzebujemy współrzędnych wierzchołków. Ustawmy te punkty w tablicy o wymiarach `liczba_wierszy x 1 x 2`, gdzie `liczba_wierszy` to liczba wierzchołków i powinna być typu `int32`. Tutaj narysujemy mały wielokąt z czterema wierzchołkami w kolorze jasnoniebieskim."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "60223ef1",
"metadata": {
"id": "60223ef1"
},
"outputs": [],
"source": [
"pts = np.array([[10,5], [20,30], [70,20], [50,10]], np.int32)\n",
"pts = pts.reshape((-1, 1, 2))\n",
"cv.polylines(img, [pts], True, (0,255,255))\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "cdba71d3",
"metadata": {
"id": "cdba71d3"
},
"source": [
"Wielokąt może wydawać się przerywany, ale jeśli powiększylibyśmy wynikowy obraz, to byśmy zobaczyli, że jest on zamknięty.\n",
"\n",
"Jeśli trzecim argumentem [`cv.polylines()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#gaa3c25f9fb764b6bef791bf034f6e26f5) jest `False`, otrzymamy niedomknięty poligon.\n",
"\n",
"Funkcja `cv.polylines()` może być używana do rysowania wielu linii. Wystarczy utworzyć listę wszystkich linii, które chcemy narysować i przekazać je do funkcji. Wszystkie linie zostaną narysowane osobno. Jest to znacznie szybszy sposób na narysowanie grupy linii niż wywołanie osobno [`cv.line()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga7078a9fae8c7e7d13d24dac2520ae4a2) dla każdej linii.\n",
"\n",
"### Tekst\n",
"\n",
"Aby umieścić tekst na obrazie, musimy określić następujące rzeczy:\n",
"\n",
"* dane tekstowe, które chcemy napisać,\n",
"* współrzędne miejsca, w którym chcemy umieścić tekst (np. lewy dolny róg, w którym zacznie się tekst),\n",
"* typ czcionki (sprawdź dokumentację [`cv.putText()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga5126f47f883d730f633d74f07456c576) dla obsługiwanych czcionek),\n",
"* rozmiar czcionki,\n",
"* pozostałe rzeczy takie jak kolor, grubość, typ linii, itp.; dla uzyskania lepszego wynikowego wyglądu zaleca się `lineType = `[`cv.LINE_AA`](https://docs.opencv.org/4.5.3/d0/de1/group__core.html#ggaf076ef45de481ac96e0ab3dc2c29a777a85fdabe5335c9e6656563dfd7c94fb4f).\n",
"\n",
"Na naszym obrazie umieścimy tekst w kolorze białym."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2de2bcf6",
"metadata": {
"id": "2de2bcf6"
},
"outputs": [],
"source": [
"cv.putText(img, 'OpenCV', (10,500), cv.FONT_HERSHEY_SIMPLEX, 4, (255,255,255), 2, cv.LINE_AA)\n",
"\n",
"plt.imshow(img)"
]
},
{
"cell_type": "markdown",
"id": "326c28ac",
"metadata": {
"id": "326c28ac"
},
"source": [
"Czasami potrzebujemy jednak dokładnych informacji o wynikowym tekście, tak aby np. odpowiednio go umiejscowić lub umieścić pod nim kontrastowe tło. Do uzyskania tych informacji pomocne są funkcje [`cv.getTextSize()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga3d2abfcb995fd2db908c8288199dba82) i [`cv.getFontScaleFromHeight()`](https://docs.opencv.org/4.5.3/d6/d6e/group__imgproc__draw.html#ga442ff925c1a957794a1309e0ed3ba2c3)."
]
},
{
"cell_type": "markdown",
"id": "eec58cd3",
"metadata": {
"id": "eec58cd3"
},
"source": [
"## Wyświetlanie obrazów poza Jupyterem\n",
"\n",
"### HighGUI\n",
"\n",
"W OpenCV obraz standardowo wyświetla się przy pomocy modułu HighGUI. Przy użyciu funkcji [`cv.imshow()`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ga453d42fe4cb60e5723281a89973ee563) możemy wyświetlić obraz w oknie. Okno automatycznie dopasowuje się do rozmiaru obrazu.\n",
"\n",
"Pierwszy argument to nazwa okna. Drugi argument to nasz obraz. Możemy utworzyć dowolną liczbę okien, ale z różnymi nazwami okien.\n",
"\n",
"```python\n",
"cv.imshow('image', image)\n",
"cv.waitKey(0)\n",
"```\n",
"\n",
"Pod Windowsem wyświetli nam się wskazany przez nas obraz, natomiast pod Linuxem mechanizm HighGUI przy wyświetlaniu obrazu udostępnia trochę więcej opcji i informacji o wyświetlanym obrazie.\n",
"\n",
"![Obraz pawiana wyświetlony przy pomocy HighGUI](img/highgui-baboon.png)\n",
"\n",
"Zwróćmy uwagę, że obraz pojawia się dopiero po poleceniu `cv.waitKey(0)` oraz będzie oczekiwał na wciśnięcie klawisza.\n",
"\n",
"[`cv.waitKey()`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ga5628525ad33f52eab17feebcfba38bd7) jest funkcją związaną z klawiaturą. Jej argumentem jest czas w milisekundach. Funkcja czeka przez określony czas na każde zdarzenie klawiatury. Jeśli w tym czasie zostanie naciśnięty dowolny klawisz, program przejdzie do dalszych operacji. Jeśli jako argument zostanie przekazane 0, to funkcja czeka bez końca na uderzenie klawisza klawiatury. Można również ustawić wykrywanie konkretnych klawiszy, takich jak naciśnięcie klawisza `<A>`, itp. Poza przetwarzaniem zdarzeń klawiatury, ta funkcja przetwarza również wiele innych zdarzeń GUI, więc **musisz** użyć jej przy wyświetleniu obrazu.\n",
"\n",
"Klikając na obraz i wciskając `<Enter>`, konsola zwróci informację liczbową o wciśniętym klawiszu, a ponadto obsługa programu wróci z powrotem do konsoli. Wydając poniższe polecenie usuniemy wszystkie okna:\n",
"\n",
"```python\n",
"cv.destroyAllWindows()\n",
"```\n",
"\n",
"[`cv.destroyAllWindows()`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ga6b7fc1c1a8960438156912027b38f481) usuwa wszystkie utworzone przez nas okna, a jeśli chcemy usunąć konkretne okno, należy użyć funkcji [`cv.destroyWindow()`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ga851ccdd6961022d1d5b4c4f255dbab34), w której jako argument podajemy dokładną nazwę okna.\n",
"\n",
"Istnieje specjalny przypadek, w którym możemy utworzyć okno i później załadować do niego obraz. W takim przypadku określamy czy rozmiar okna jest zmienny, czy nie. Robi się to za pomocą funkcji [`cv.namedWindow()`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ga5afdf8410934fd099df85c75b2e0888b). Domyślna flaga to [`cv.WINDOW_AUTOSIZE`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ggabf7d2c5625bc59ac130287f925557ac3acf621ace7a54954cbac01df27e47228f), ale jeśli określimy flagę jako [`cv.WINDOW_NORMAL`](https://docs.opencv.org/4.5.3/d7/dfc/group__highgui.html#ggabf7d2c5625bc59ac130287f925557ac3a29e45c5af696f73ce5e153601e5ca0f1), to możemy zmienić rozmiar okna. Będzie to pomocne, gdy obraz jest zbyt duży:\n",
"\n",
"```python\n",
"cv.namedWindow('image', cv.WINDOW_NORMAL)\n",
"cv.imshow('image', image)\n",
"cv.waitKey(0)\n",
"cv.destroyAllWindows()\n",
"```\n",
"\n",
"Interfejs HighGUI czasami pomaga w debugowaniu, ale trzeba mieć też świadomość, że nie jest on specjalnie rozbudowany i zasadniczo nie służy do budowania zaawansowanych interfejsów graficznych. HighGUI obsługuje sygnały idące nie tylko z klawiatury, ale również myszy, a ponadto pozawala na umieszczenie np. sliderów. Krótkie przykłady znajdują się w samouczkach opisujących [`cv.setMouseCallback()`](https://docs.opencv.org/4.5.3/db/d5b/tutorial_py_mouse_handling.html) oraz [`cv.getTrackbarPos()` i `cv.createTrackbar()`](https://docs.opencv.org/4.5.3/d9/dc8/tutorial_py_trackbar.html).\n",
"\n",
"### Matplotlib\n",
"\n",
"Po przekonwertowaniu obrazu do formatu RGB wyświetlamy go przy pomocy `pyplot`. Używamy przy tym funkcji [`matplotlib.pyplot.imshow()`](https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.imshow) oraz [`matplotlib.pyplot.show()`](https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show):\n",
"\n",
"```python\n",
"plt.imshow(image2)\n",
"plt.show()\n",
"```\n",
"\n",
"Funkcja [`matplotlib.pyplot.show()`](https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.show) posiada opcjonalny argument `block` (domyślna wartość `True`), która kontroluje czy funkcja jest blokująca:\n",
"\n",
"```python\n",
"plt.imshow(image2)\n",
"plt.show(False)\n",
"```\n",
"\n",
"lub krócej:\n",
"\n",
"```python\n",
"plt.imshow(image2)\n",
"plt.show(0)\n",
"```\n",
"\n",
"**Uwaga**: jeżeli w skrypcie ustawimy argument `block` na `False`, to po uruchomieniu skryptu najprawdopodobniej nie zobaczymy wynikowego obrazu/diagramu, ponieważ skrypt zacznie wykonywać dalsze polecenia i np. zakończy działanie programu. Ustawienie argumentu `block` na `False` najlepiej sprawdza się podczas pracy/eksperymentowania w konsoli interpretera Pythona.\n",
"\n",
"## Moduły\n",
"\n",
"[Lista modułów OpenCV](https://docs.opencv.org/4.5.3/) jest dość długa i zawiera pakiety algorytmów m.in. dla przetwarzania obrazów, uczenia maszynowego, głębokich sieci neuronowych, fotografii obliczeniowej, dedykowane rozwiązania dla CUDA, przepływ optyczny, operacje dla obrazów w logice rozmytej, przetwarzania RGB-D i wiele, wiele innych. Z poziomu Pythona dostęp do poszczególnych algorytmów odbywa się bezpośrednio przez moduł `cv2` (u nas alias `cv`). Część klas i funkcji ma przedrostek oznaczający moduł, np. `cv.bgsegm_BackgroundSubtractorCNT(...)` oznacza klasę [BackgroundSubtractorCNT](https://docs.opencv.org/4.5.3/de/dca/classcv_1_1bgsegm_1_1BackgroundSubtractorCNT.html) z modułu [bgsegm](https://docs.opencv.org/4.5.3/df/d5d/namespacecv_1_1bgsegm.html) zawierającym udoskonalone metody segmentacji pomiędzy tłem a pierwszym planem.\n",
"\n",
"## Ćwiczenie 1\n",
"\n",
"Wczytaj plik [`img/soyjaks.jpg`](https://knowyourmeme.com/memes/two-soyjaks-pointing) i spróbuj odtworzyć poniższy obrazek."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5F_V30CUy-i7",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 17024,
"status": "ok",
"timestamp": 1666007708904,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "5F_V30CUy-i7",
"outputId": "0faa68db-5115-43c9-f9b2-43aa4a771c82"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Mounted at /content/drive\n"
]
}
],
"source": [
"from google.colab import drive\n",
"drive.mount('/content/drive')\n",
"%cd /content/drive/My Drive/aitech-wko-pub"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "PCVjMgv_zB7m",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 242,
"status": "ok",
"timestamp": 1666007758111,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "PCVjMgv_zB7m",
"outputId": "b2618783-2585-48ab-d1df-d5ec484ba547"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"/content/drive/My Drive/aitech-wko-pub\n"
]
}
],
"source": [
"%cd /content/drive/My Drive/aitech-wko-pub"
]
},
{
"cell_type": "code",
"execution_count": 48,
"id": "ff692a8e",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 265
},
"executionInfo": {
"elapsed": 700,
"status": "ok",
"timestamp": 1666008786851,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "ff692a8e",
"outputId": "743a4a6e-9786-4001-c15e-bab7412b03f7"
},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.image.AxesImage at 0x7fd69e926b90>"
]
},
"execution_count": 48,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"import matplotlib\n",
"%matplotlib inline\n",
"\n",
"image = cv.imread(\"img/soyjaks.jpg\", cv.IMREAD_COLOR)\n",
"image2 = cv.cvtColor(image, cv.COLOR_BGR2RGB)\n",
"cv.circle(image2, (320,220), 55, (143,0,255), 10)\n",
"cv.putText(image2, 'OMG not chikenz!', (10,90), cv.FONT_HERSHEY_SIMPLEX, 2, (255,0,0), 7, cv.LINE_AA)\n",
"\n",
"plt.axis('off')\n",
"plt.imshow(image2)"
]
},
{
"cell_type": "markdown",
"id": "279f1a5d",
"metadata": {
"id": "279f1a5d"
},
"source": [
"![Two Soyjaks Pointing](img/soyjaks-final.png)"
]
},
{
"cell_type": "markdown",
"id": "288aa117",
"metadata": {
"id": "288aa117"
},
"source": [
"## Ćwiczenie 2\n",
"\n",
"Załaduj obrazy `img/pipe.png` oraz `img/man-without-pipe.png` i wykonaj operacje tak, aby uzyskać poniższy obraz."
]
},
{
"cell_type": "code",
"execution_count": 78,
"id": "2cb83db1",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 265
},
"executionInfo": {
"elapsed": 648,
"status": "ok",
"timestamp": 1666012106380,
"user": {
"displayName": "Cezary Gałązkiewicz",
"userId": "01409497901784152256"
},
"user_tz": -120
},
"id": "2cb83db1",
"outputId": "c3ad7496-2278-40a0-9a51-336108bffe6a"
},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.image.AxesImage at 0x7fd69dfbb3d0>"
]
},
"execution_count": 78,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"image1 = cv.imread(\"img/pipe.png\", cv.IMREAD_UNCHANGED)\n",
"image_pipe = cv.cvtColor(image1, cv.COLOR_BGRA2RGBA)\n",
"image_pipe = image_flipped = cv.flip(image_pipe, 1)\n",
"image_pipe = cv.resize(image_pipe, None, fx=0.5, fy=0.5, interpolation=cv.INTER_CUBIC)\n",
"\n",
"image2 = cv.imread(\"img/man-without-pipe.png\", cv.IMREAD_COLOR)\n",
"image_man = cv.cvtColor(image2, cv.COLOR_BGR2RGB)\n",
"\n",
"background_width = image_man.shape[1]\n",
"background_height = image_man.shape[0]\n",
"x, y = 90, 280\n",
"img_h, img_w = image_pipe.shape[0], image_pipe.shape[1]\n",
"\n",
"if x + img_w > background_width:\n",
" w = background_width - x\n",
" image_pipe = image_pipe[:, :w]\n",
"\n",
"if y + img_h > background_height:\n",
" h = background_height - y\n",
" image_pipe = image_pipe[:h]\n",
"\n",
"image_pipe_not_trans = image_pipe[..., :3]\n",
"alpha_mask = image_pipe[..., 3:] / 255.0\n",
"\n",
"image_man[y:y+img_h, x:x+img_w] = (1.0 - alpha_mask) * image_man[y:y+img_h, x:x+img_w] + alpha_mask * image_pipe_not_trans\n",
"\n",
"\n",
"plt.axis('off')\n",
"plt.imshow(image_pipe)\n",
"plt.imshow(image_man)"
]
},
{
"cell_type": "markdown",
"id": "7baba75b",
"metadata": {
"id": "7baba75b"
},
"source": [
"![Człowiek z fajką](img/man-with-pipe.png)"
]
},
{
"cell_type": "markdown",
"id": "f0b793fc",
"metadata": {
"id": "f0b793fc"
},
"source": [
"## Co dalej?\n",
"\n",
"Przygotuj środowisko pracy instalując wygodne dla Ciebie IDE, np. [PyCharm](https://www.jetbrains.com/pycharm/) (są darmowe studenckie licencje), [Visual Studio](https://visualstudio.microsoft.com) z dodatkiem [Python Tools](https://microsoft.github.io/PTVS/) lub [Visual Studio Code](https://code.visualstudio.com/). Sprawdź czy możesz zaimportować OpenCV, ew. stwórz wirtualne środowisko i doinstaluj niezbędne paczki."
]
},
{
"cell_type": "markdown",
"id": "4a713f31",
"metadata": {
"id": "4a713f31"
},
"source": [
"----\n",
"\n",
"Źródło:\n",
"\n",
"* OpenCV, [Getting Started with Images](https://docs.opencv.org/4.5.3/dc/d2e/tutorial_py_image_display.html).\n",
"* OpenCV, [Drawing Functions in OpenCV](https://docs.opencv.org/4.5.3/dc/da5/tutorial_py_drawing_functions.html).\n",
"* P. Pandey, [Face Detection with Python using OpenCV](https://www.datacamp.com/community/tutorials/face-detection-python-opencv)."
]
}
],
"metadata": {
"author": "Andrzej Wójtowicz",
"colab": {
"collapsed_sections": [
"185a63ad",
"c561f1ea",
"54e22dd0",
"2b23ae8a",
"bb1a0101",
"cdcb4b70",
"60678ca6",
"1219997d",
"cdba71d3",
"f0b793fc"
],
"provenance": []
},
"email": "andre@amu.edu.pl",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"lang": "pl",
"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.8.10"
},
"subtitle": "01. Wprowadzenie do widzenia komputerowego [laboratoria]",
"title": "Widzenie komputerowe",
"year": "2021"
},
"nbformat": 4,
"nbformat_minor": 5
}