s444380-wko/wko-06.ipynb

880 lines
1.1 MiB
Plaintext
Raw Normal View History

2023-01-22 20:08:13 +01:00
{
"cells": [
{
"cell_type": "markdown",
"id": "819ce420",
"metadata": {},
"source": [
"![Logo 1](img/aitech-logotyp-1.jpg)\n",
"<div class=\"alert alert-block alert-info\">\n",
"<h1> Widzenie komputerowe </h1>\n",
"<h2> 06. <i>Rozpoznawanie i segmentacja obrazów</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": "a6dc5acc",
"metadata": {},
"source": [
"W poniższych materiałach zobaczymy w jaki sposób możemy klasycznym podejściem rozpoznawać ludzi na zdjęciach, a ponadto w jaki sposób szybko podzielić obraz na elementy znajdujące się na pierwszym planie i w tle obrazu.\n",
"\n",
"Na początku załadujmy niezbędne biblioteki."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e45bb312",
"metadata": {},
"outputs": [],
"source": [
"import cv2 as cv\n",
"import numpy as np\n",
"import sklearn.svm\n",
"import sklearn.metrics\n",
"import matplotlib.pyplot as plt\n",
"%matplotlib inline\n",
"import os\n",
"import random"
]
},
{
"cell_type": "markdown",
"id": "5b757675",
"metadata": {},
"source": [
"Naszym głównym celem będzie rozpoznawanie ludzi na zdjęciach przy pomocy klasycznej metody *histogram of oriented gradients* (HOG). Krótko mówiąc, dla danego zdjęcia chcemy uzyskać wektor cech, który będziemy mogli wykorzystać w klasyfikatorze SVM. Szczegóły znajdują się w *6.3.2 Pedestrian detection* R. Szeliski (2022) *Computer Vision: Algorithms and Applications*, natomiast tutaj zobrazujemy techniczne wykorzystanie tej metody.\n",
"\n",
"# Klasyfikacja obrazów przy użyciu HOG i SVM\n",
"\n",
"Spróbjemy zbudować klasyfikator, który wskazuje czy na zdjęciu znajduje się osoba z okularami czy bez okularów. Rozpakujmy zbiór danych, z którego będziemy korzystali:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "7b953b82",
"metadata": {},
"outputs": [],
"source": [
"!cd datasets && unzip -qo glasses.zip"
]
},
{
"cell_type": "markdown",
"id": "f4a457f3",
"metadata": {},
"source": [
"Następnie wczytujemy dane i dzielimy je na dwa zbiory w proporcjach 80/20:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "737d95c1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train data: 1272, test data: 319\n"
]
}
],
"source": [
"dataset_dir = \"datasets/glasses\"\n",
"images_0 = os.listdir(f\"{dataset_dir}/with\")\n",
"images_0 = [f\"{dataset_dir}/with/{x}\" for x in images_0]\n",
"images_1 = os.listdir(f\"{dataset_dir}/without\")\n",
"images_1 = [f\"{dataset_dir}/without/{x}\" for x in images_1]\n",
"images = images_0 + images_1\n",
"random.seed(1337)\n",
"random.shuffle(images)\n",
"\n",
"train_data = []\n",
"test_data = []\n",
"train_labels = []\n",
"test_labels = []\n",
"\n",
"splitval = int((1-0.2)*len(images))\n",
"\n",
"for x in images[:splitval]:\n",
" train_data.append(cv.imread(x, cv.IMREAD_COLOR))\n",
" train_labels.append(x.split(\"/\")[2])\n",
" \n",
"for x in images[splitval:]:\n",
" test_data.append(cv.imread(x, cv.IMREAD_COLOR))\n",
" test_labels.append(x.split(\"/\")[2])\n",
" \n",
"d_labels = {\"with\": 0, \"without\": 1}\n",
" \n",
"train_labels = np.array([d_labels[x] for x in train_labels])\n",
"test_labels = np.array([d_labels[x] for x in test_labels])\n",
"\n",
"print(f\"Train data: {len(train_data)}, test data: {len(test_data)}\")"
]
},
{
"cell_type": "markdown",
"id": "265147e3",
"metadata": {},
"source": [
"Poniżej znajduje się kilka przykładowych zdjęć."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "e0595915",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x200 with 5 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(10,2))\n",
"for i in range(5):\n",
" plt.subplot(151 + i)\n",
" plt.imshow(train_data[i][:,:,::-1]);"
]
},
{
"cell_type": "markdown",
"id": "e05e27e8",
"metadata": {},
"source": [
"Tworzymy deskryptor HOG przy pomocy funkcji [`cv.HOGDescriptor()`](https://docs.opencv.org/4.5.3/d5/d33/structcv_1_1HOGDescriptor.html). Metodą [`compute()`](https://docs.opencv.org/4.5.3/d5/d33/structcv_1_1HOGDescriptor.html#a38cd712cd5a6d9ed0344731fcd121e8b) tworzymy wektory cech, które posłużą nam jako dane wejściowe do klasyfikatora. Poniżej znajduje się również przykładowa konfiguracja deskryptora:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "f21e8924",
"metadata": {},
"outputs": [],
"source": [
"hp_win_size = (96, 32)\n",
"hp_block_size = (8, 8)\n",
"hp_block_stride = (8, 8)\n",
"hp_cell_size = (4, 4)\n",
"hp_n_bins = 9\n",
"hp_deriv_aperture = 0\n",
"hp_win_sigma = 4.0\n",
"hp_histogram_norm_type = 1\n",
"hp_l2_hys_threshold = 0.2\n",
"hp_gamma_correction = True\n",
"hp_n_levels = 64\n",
"hp_signed_gradient = True\n",
"\n",
"hog_descriptor = cv.HOGDescriptor(\n",
" hp_win_size, hp_block_size, hp_block_stride, hp_cell_size, \n",
" hp_n_bins, hp_deriv_aperture, hp_win_sigma, \n",
" hp_histogram_norm_type, hp_l2_hys_threshold, \n",
" hp_gamma_correction, hp_n_levels, hp_signed_gradient)\n",
"\n",
"train_hog = np.vstack([hog_descriptor.compute(x).ravel() for x in train_data])\n",
"test_hog = np.vstack([hog_descriptor.compute(x).ravel() for x in test_data])"
]
},
{
"cell_type": "markdown",
"id": "755b8ebe",
"metadata": {},
"source": [
"Do klasyfikacji użyjemy klasyfikatora SVM. Możemy użyć implementacji znajdującej się w module [`cv.ml`](https://docs.opencv.org/4.5.3/d1/d2d/classcv_1_1ml_1_1SVM.html):"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "b46783d4",
"metadata": {},
"outputs": [],
"source": [
"model = cv.ml.SVM_create()\n",
"model.setGamma(0.02)\n",
"model.setC(2.5)\n",
"model.setKernel(cv.ml.SVM_RBF)\n",
"model.setType(cv.ml.SVM_C_SVC)"
]
},
{
"cell_type": "markdown",
"id": "d8f47c54",
"metadata": {},
"source": [
"Trenujemy model:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "810f9a1e",
"metadata": {},
"outputs": [],
"source": [
"model.train(np.array(train_hog), cv.ml.ROW_SAMPLE, train_labels);"
]
},
{
"cell_type": "markdown",
"id": "69d39eee",
"metadata": {},
"source": [
"Sprawdzamy wynik na danych testowych:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "763b6dc7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ACC: 93.10 %\n"
]
}
],
"source": [
"predictions = model.predict(test_hog)[1].ravel()\n",
"accuracy = (test_labels == predictions).mean()\n",
"print(f\"ACC: {accuracy * 100:.2f} %\")"
]
},
{
"cell_type": "markdown",
"id": "2dd04ec5",
"metadata": {},
"source": [
"Możemy również użyć implementacji klasyfikatora znajdującej się w bibliotece [`scikit-learn`](https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html):"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "13b7ba1c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ACC: 93.10 %\n"
]
}
],
"source": [
"model = sklearn.svm.SVC(C=2.5, gamma=0.02, kernel='rbf')\n",
"model.fit(train_hog, train_labels)\n",
"\n",
"predictions = model.predict(test_hog)\n",
"accuracy = (test_labels == predictions).mean()\n",
"print(f\"ACC: {accuracy * 100:.2f} %\")"
]
},
{
"cell_type": "markdown",
"id": "2259c310",
"metadata": {},
"source": [
"# Rozpoznawanie ludzi\n",
"\n",
"Powyższą metodykę klasyfikcji możemy zastosować do rozpoznawania obiektów na zdjęciach, np. ludzi. W tym wypadku będziemy chcieli wskazać gdzie na zdjęciu znajduje się dany obiekt lub obiekty.\n",
"\n",
"Rozpocznijmy od rozpakowania zbioru danych:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "d8497390",
"metadata": {},
"outputs": [],
"source": [
"!cd datasets && unzip -qo inria-person-sub.zip"
]
},
{
"cell_type": "markdown",
"id": "30374bad",
"metadata": {},
"source": [
"Wczytujemy dane, które są już podzielone na dwa zbiory:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "978d77cf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train data: 1457, test data: 560\n"
]
}
],
"source": [
"dataset_dir = \"datasets/INRIAPerson\"\n",
"\n",
"images_train_0 = os.listdir(f\"{dataset_dir}/train_64x128_H96/negPatches\")\n",
"images_train_0 = [f\"{dataset_dir}/train_64x128_H96/negPatches/{x}\" for x in images_train_0]\n",
"images_train_1 = os.listdir(f\"{dataset_dir}/train_64x128_H96/posPatches\")\n",
"images_train_1 = [f\"{dataset_dir}/train_64x128_H96/posPatches/{x}\" for x in images_train_1]\n",
"\n",
"images_test_0 = os.listdir(f\"{dataset_dir}/test_64x128_H96/negPatches\")\n",
"images_test_0 = [f\"{dataset_dir}/test_64x128_H96/negPatches/{x}\" for x in images_test_0]\n",
"images_test_1 = os.listdir(f\"{dataset_dir}/test_64x128_H96/posPatches\")\n",
"images_test_1 = [f\"{dataset_dir}/test_64x128_H96/posPatches/{x}\" for x in images_test_1]\n",
"\n",
"train_data = []\n",
"test_data = []\n",
"train_labels = []\n",
"test_labels = []\n",
"\n",
"for x in images_train_0:\n",
" img = cv.imread(x, cv.IMREAD_COLOR)\n",
" if img is not None:\n",
" train_data.append(img)\n",
" train_labels.append(0)\n",
"\n",
"for x in images_train_1:\n",
" img = cv.imread(x, cv.IMREAD_COLOR)\n",
" if img is not None:\n",
" train_data.append(img)\n",
" train_labels.append(1)\n",
" \n",
"for x in images_test_0:\n",
" img = cv.imread(x, cv.IMREAD_COLOR)\n",
" if img is not None:\n",
" test_data.append(img)\n",
" test_labels.append(0)\n",
"\n",
"for x in images_test_1:\n",
" img = cv.imread(x, cv.IMREAD_COLOR)\n",
" if img is not None:\n",
" test_data.append(img)\n",
" test_labels.append(1)\n",
"\n",
"print(f\"Train data: {len(train_data)}, test data: {len(test_data)}\")"
]
},
{
"cell_type": "markdown",
"id": "9bf41d6e",
"metadata": {},
"source": [
"Poniżej znajduje się kilka przykładowych zdjęć ze zbioru:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "f29d47c1",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x200 with 6 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.figure(figsize=(10,2))\n",
"for i in range(3):\n",
" plt.subplot(161 + i)\n",
" plt.imshow(train_data[i][:,:,::-1]);\n",
"for i in range(3):\n",
" plt.subplot(164 + i)\n",
" plt.imshow(train_data[-(i+1)][:,:,::-1]);"
]
},
{
"cell_type": "markdown",
"id": "57cec468",
"metadata": {},
"source": [
"Tworzymy deskryptor i wektory cech:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "d5248df2",
"metadata": {},
"outputs": [],
"source": [
"hp_win_size = (64, 128)\n",
"hp_block_size = (16, 16)\n",
"hp_block_stride = (8, 8)\n",
"hp_cell_size = (8, 8)\n",
"hp_n_bins = 9\n",
"hp_deriv_aperture = 1\n",
"hp_win_sigma = -1\n",
"hp_histogram_norm_type = 0\n",
"hp_l2_hys_threshold = 0.2\n",
"hp_gamma_correction = True\n",
"hp_n_levels = 64\n",
"hp_signed_gradient = False\n",
"\n",
"hog_descriptor = cv.HOGDescriptor(\n",
" hp_win_size, hp_block_size, hp_block_stride, hp_cell_size, \n",
" hp_n_bins, hp_deriv_aperture, hp_win_sigma, \n",
" hp_histogram_norm_type, hp_l2_hys_threshold, \n",
" hp_gamma_correction, hp_n_levels, hp_signed_gradient)\n",
"\n",
"train_hog = np.vstack([hog_descriptor.compute(x).ravel() for x in train_data])\n",
"test_hog = np.vstack([hog_descriptor.compute(x).ravel() for x in test_data])"
]
},
{
"cell_type": "markdown",
"id": "c6782aa9",
"metadata": {},
"source": [
"Następnie tworzymy klasyfikator:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "8f6108ed",
"metadata": {},
"outputs": [],
"source": [
"model = cv.ml.SVM_create()\n",
"model.setGamma(0)\n",
"model.setC(0.01)\n",
"model.setKernel(cv.ml.SVM_LINEAR)\n",
"model.setType(cv.ml.SVM_C_SVC)\n",
"model.setTermCriteria((cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 1000, 1e-3))"
]
},
{
"cell_type": "markdown",
"id": "bbfbde58",
"metadata": {},
"source": [
"Uczymy model:"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "afd0bbb4",
"metadata": {},
"outputs": [],
"source": [
"model.train(np.array(train_hog), cv.ml.ROW_SAMPLE, np.array(train_labels));"
]
},
{
"cell_type": "markdown",
"id": "09626eed",
"metadata": {},
"source": [
"Sprawdzamy jakość klasyfikacji:"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "fa3be6b6",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"ACC: 96.96 %\n"
]
}
],
"source": [
"predictions = model.predict(test_hog)[1].ravel()\n",
"accuracy = (test_labels == predictions).mean()\n",
"print(f\"ACC: {accuracy * 100:.2f} %\")"
]
},
{
"cell_type": "markdown",
"id": "c6df6682",
"metadata": {},
"source": [
"Poniżej znajduje się podejście przy pomocy biblioteki *scikit-learn*:"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "7b3de8d1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Accuracy: 96.96 %\n",
"Precision: 93.55 %\n",
"Recall: 88.78 %\n"
]
}
],
"source": [
"model2 = sklearn.svm.SVC(C=0.01, gamma='auto', kernel='linear', max_iter=1000)\n",
"model2.fit(train_hog, train_labels)\n",
"\n",
"predictions = model2.predict(test_hog)\n",
"accuracy = (test_labels == predictions).mean()\n",
"print(f\"Accuracy: {sklearn.metrics.accuracy_score(test_labels, predictions) * 100:.2f} %\")\n",
"print(f\"Precision: {sklearn.metrics.precision_score(test_labels, predictions) * 100:.2f} %\")\n",
"print(f\"Recall: {sklearn.metrics.recall_score(test_labels, predictions) * 100:.2f} %\")"
]
},
{
"cell_type": "markdown",
"id": "6e84c568",
"metadata": {},
"source": [
"Mając teraz wyuczony model, chcielibyśmy sprawdzić czy np. na zdjęciu `img/pedestrians.jpg` znajdują się ludzie, tak aby uzyskać ew. obramowania z ich występowaniem. W pierwszej kolejności w naszym deskryptorze HOG ustawiamy współczynniki klasfikatora SVM przy pomocy metody [`setSVMDetector()`](https://docs.opencv.org/4.5.3/d5/d33/structcv_1_1HOGDescriptor.html#a6de5ac55631eed51e36278cde3a2c159). Następnie przy pomocy metody [`detectMultiScale()`](https://docs.opencv.org/4.5.3/d5/d33/structcv_1_1HOGDescriptor.html#a91e56a2c317392e50fbaa2f5dc78d30b) znajdujemy wyszukiwane obiekty (ludzi) w różnych skalach."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "d6458103",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAZwAAAH+CAYAAACofbhoAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9aawsWXbfi/3W3jsiMvOM996qO9TQczdFDST1SKkfYRnQ0BZFAYIkC4YIEAYh6ImA4BYs9AdZBCTKpAUTEASDlkSAHzwIepBsfTBAGIJBP5myH9976tccREoUp56Hqlt3PGNOEbH3Xv6wdkTmufdW1S0O3V1duRrV5548kZGRmTv2Wuu//uu/RFWVne1sZzvb2c5+n819oy9gZzvb2c529t6wncPZ2c52trOdfV1s53B2trOd7WxnXxfbOZyd7WxnO9vZ18V2DmdnO9vZznb2dbGdw9nZzna2s519XWzncHa2s53tbGdfF9s5nJ3tbGc729nXxXYOZ2c729nOdvZ1sZ3D2dnOdraznX1d7BvqcH7qp36KD3zgA0wmEz7+8Y/zC7/wC9/Iy9nZzna2s539Pto3zOH863/9r/nUpz7FP/gH/4D/8B/+A9/5nd/J933f9/HgwYNv1CXtbGc729nOfh9NvlHinR//+Mf5Y3/sj/HP/tk/AyDnzKuvvsrf+lt/i7/7d//uWz4358zdu3c5ODhARL4el7uzne1sZzt7E1NVLi8veemll3DuzfOY8HW8ptG6ruOXf/mX+ZEf+ZHxMeccn/jEJ/j0pz/91PFt29K27fj766+/zh/8g3/w63KtO9vZzna2s+ezr33ta7zyyitv+vdviMN59OgRKSVu3bp15fFbt27xW7/1W08d/xM/8RP82I/92FOPf+nX/wMHB/vv6LWflc6JgmTl5OFDvvS5L9CvW1AHCFkV2UoCoxc6p7QipDBFJaDiEAScQ1QRpzhABZCrL6oZVO0/APHDRdh1AIjYf678ojikXL1g1+OH4wBIyOY0m5OMjylOEk6UKgSOD4+4fec2e5MZYBnjEJXs8sWd7ex3YFpudbm6x6gqKWfalHn06Iz5csnhwYwb146Z1RWqijhHGxr+L//m3/G//6//n/QEXmrg//y/+1/zR16+SaW2N3Qolxr41P/x/8p/+9UTFm3PQXfOP/yv/uf8pT/+XdQ+cG/Z8l/9b3+S//Q4cvjBj3H0wiHH+1Om4hBR1m3L9UngxemE2PUogZQVIeEqT4/jvOtZrtbcOb5GiJGKTBM8axVeayO/cf8hi4sFq6++xvF+zcHBlNguuPt//ykODg7e8mP6hjicd2o/8iM/wqc+9anx94uLC1599VUODvY5PHzrN/ik6fh/xQRzEknpl0v2ZlM6cYg6UEFRyFoOhN6Dd+AEkp+Qi8NBPOIE8zGKEy2LT65s4jnb6w8Ox3nZvjIQtXOIjD/N+dkxDh1/ipgrcpJGlyRl5Q+/2Y8MmnACIQS6viP2Pc1RzWQyMYcjwoCuDjClqu4gy53t7Hls2+GoWqA43k+OhOBdzd5ywdHBHsdH+3jUbnvn6PyEqq7xVU2iQn2kmcw4ONynKV6sR4GaMJ0hzbLsUTWTyZTDwwOaEOibCe//wMssb1Zw/UWaWcN+CBz6gBdYOrixF7i1P6XtajI1OYHkSDOpSQiTxZJLFa5NJrgcabyjcjB1nssuUV2c49oeCTX4Glc1aFyX9/rW+8U3xOG88MILeO+5f//+lcfv37/P7du3nzq+aRqapnlnL/KEUxkes4RDEAUtKYUKiBcySsqKE0dO5hUyCtk+yIySk335xW+gHpIomYRkcDicDie1nT+LoiJ2/PhEe+3Bl6mYExuu2xVXJZIRNYcAStJkzqGkRFm0PMUVZ7U5iVCyJhUUT0bQBDn2vHHvHpoS169dIzjPbH8PkNHJ7Oby7Wxnv3sbkAnNiuZIyonlesX+LOCqgDiHkpGcyX3c2jc8se8hQy4Yh5RoWQX6FFFxeO+oXICciX1PFqWeBQ40UFWOGqGRCvEVUZRWMxoCLZlMpq4gppbghP3gWXUR2o7cdaSU6bqe3HgySiXCtaomxEhKHXjFe8ELTIJ/y89hsG8IS62ua777u7+bn/u5nxsfyznzcz/3c3zv937v7/vrWxZy9a0r9sUmzaRcoK+s5KxkhayZnCFnRVXHnylnUlayKkkhKqTikLJmEvb4+B8QVUlqzi0N/y7PjWpZUFZFsde58vwr54GUHUmFqEpUIamQVcjK5tzY8RnICEmhbXvu3b/PF7/4Re7efZ35fI7mbJ/FztnsbGfv2Ia7ZkAnnMgY8eccWayWnJ5d8ODhY9Zt/9Szc852DyqoZlLOBucP4Ec5TrcCSu88h/t7hnQUWFyT4gX2pxOmk4ZQVcxXHZ/97Of5wm9/kfv3z7hcCZfRM+8VrSekqmah0IcaaSrCpMZPamIdSHVNqhtacXRZmUxnOOc3AIpSAuK3t28YpPapT32KH/qhH+J7vud7+ON//I/zkz/5kywWC/7aX/trvzcv8Kz3/0Q9ZdvGzT1lNCVLPdSyGikpci7RhW3cmKOBEiu4AmUpokLEMihVJZdcWxHI2wtGS6HHfhPFvj1DzVB7xviaIGiWspCHhS24LDhn15mHt652EstytEBtlmWRM20fST3kFMk505ye0jQNIVTDB7Kzne3see1t9tuUFFVhtndA362J5XeDzA3eiCmNwZ4q5GR385ueWsE5T9M0iBOcODyZmoAXj1SB7B2PTk55/auvcf9LXyatlszvPeYL16+hOCoPVRCCV/YO9hHveXRywvUbN1iL4/TykvV6RRU8BwfHzHFcdIkk/mqt6jk/pm+Yw/mrf/Wv8vDhQ370R3+Ue/fu8V3f9V387M/+7FNEgq+XqVqWknMmR4PHQGwDt73a/u4g48xBpVT+LRjuZrBWYohD8kgQGKAuzduImkFgqsBYs9GRPDBWbkRQKTCgZnKJnAyxS4jY49glD8/a+mkOxzmx95UV5xwxJdZtB+JYrVtStrQ5l0xnRyHY2c5+d2YOxO6pdRdJBBKOtjfExDIDC0YzoM6cj5VtNpB/iUHNthhCIkLX9SMMnvqMdxVNs8fp+ZxHJyc8vHufywcnpOUKupbT9WPOTy/RFEEjQgTprDYdPH3MfHWyT7jxIrFu0ITtT+E+6gO5nuCq6hkF8be3byhp4JOf/CSf/OQnv5GXcMVU1Tb0nEsdxhX8tMBcQFYhaSpZS0Y0WfEuO5Bsm/+wIlwq2cVQ11E0l1pQyV/UmfMyb+Nwmq84o4HoNmasmkEwTLeQA5yAl63jZMie7DxOFScZlwXv/XAAiuCcR9Uc0JNp8Y4vsLOdvTMb2acDeQBzCm3b8/DhCZddouvW7E9qXjg6RJwMOMSGLOTkifuxFHphPJ+IA82oZrquxRi1gPdcLFZ87vUH3L24pJsvIDtEAq6ZbhAQEQjBSgt9j67W9P0SNAKOtlkzuXGT63deAfFkFdqUSN6x6DOkoRhdru85Yfh3BUvt98+e5CxbTGERvkDOxclQCAQQgeRAvUNyRjDnpK7UgcTS4uwcLmckq0FguaBlpbYiWDST1ZELk8C5AsuV5ziguAeGLMUsj85lcEoey3q8camLwxlcX0Y1gQiuEULwIEIbM9PJlNneHtdv3KAKFZp3zLSdvYdsUyL5PQywBgdhMH0InsVyzYNHJ5x1PScnD3nl9o2rCYII6mS8BhEZI0kd0hsnBsPh8CEgXUdKadwDcMKy7fjsV77C6w87eiqIxqB1LpBTZ60bziHO246SIz40qKxJCZwarqM5cW025SN3buKDI2bHOiU6UV47veTiYkksztHgv/Rcn8x7z+G8RR0HCnaq2ehnWUsNh+JwhCwWSWjOSEoGt6mUbEjKcZksDlDCwDDJguQRm7MMQy0dzjIstMFpOByCR0q/jVGgtyMJJxuH48DwWxVbMGONZ0OQyAWnC+NdJVy/do0PfORDTKZTNMWxZrSD0nb2XjRjaML2+n8SNdp2StsZzObf28G+IGJQffCBo2vXmZ+e0cZIr6kEkDoUXUc6Nc4RU0cf44h2jCx
"text/plain": [
"<Figure size 600x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"image = cv.imread(\"img/pedestrians.jpg\", cv.IMREAD_COLOR)\n",
"scale = 600 / image.shape[0]\n",
"image = cv.resize(image, None, fx=scale, fy=scale)\n",
"\n",
"support_vectors = model.getSupportVectors()\n",
"rho, _, _ = model.getDecisionFunction(0)\n",
"detector = np.zeros(support_vectors.shape[1] + 1, dtype=support_vectors.dtype)\n",
"detector[:-1] = -support_vectors[:]\n",
"detector[-1] = rho\n",
"\n",
"hog_descriptor.setSVMDetector(detector)\n",
"\n",
"locations, weights = hog_descriptor.detectMultiScale(\n",
" image, winStride=(8, 8), padding=(32, 32), scale=1.05,\n",
" finalThreshold=2, hitThreshold=1.0)\n",
"\n",
"for location, weight in zip(locations, weights):\n",
" x1, y1, w, h = location\n",
" x2, y2 = x1 + w, y1 + h\n",
" cv.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), thickness=3, lineType=cv.LINE_AA)\n",
" cv.putText(image, f\"{weight[0]:.2f}\", (x1,y1), cv.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2, cv.LINE_AA)\n",
"\n",
"plt.figure(figsize=(6,6))\n",
"plt.imshow(image[:,:,::-1]);"
]
},
{
"cell_type": "markdown",
"id": "cd287c92",
"metadata": {},
"source": [
"Coś nam nawet udało się wykryć jak na tak niewielki zbiór danych uczących ;) Z drugiej strony, dwie osoby na pierwszym planie zostały pominięte, a osoba po prawej jest dyskusyjna jeśli chodzi o zakres oznaczenia.\n",
"\n",
"W OpenCV dostępny jest domyślny klasyfikator w funkcji [`HOGDescriptor_getDefaultPeopleDetector()`](https://docs.opencv.org/4.5.3/d5/d33/structcv_1_1HOGDescriptor.html#a9c7a0b2aa72cf39b4b32b3eddea78203) i poniżej możemy zobaczyć jak sobie radzi na badanym zdjęciu:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "57a745c9",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 600x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"image = cv.imread(\"img/pedestrians.jpg\", cv.IMREAD_COLOR)\n",
"scale = 600 / image.shape[0]\n",
"image = cv.resize(image, None, fx=scale, fy=scale)\n",
"\n",
"hog_dflt_descriptor = cv.HOGDescriptor(\n",
" hp_win_size, hp_block_size, hp_block_stride, hp_cell_size, \n",
" hp_n_bins, hp_deriv_aperture, hp_win_sigma, \n",
" hp_histogram_norm_type, hp_l2_hys_threshold, \n",
" hp_gamma_correction, hp_n_levels, hp_signed_gradient)\n",
"\n",
"detector_dflt = cv.HOGDescriptor_getDefaultPeopleDetector()\n",
"hog_dflt_descriptor.setSVMDetector(detector_dflt)\n",
"\n",
"locations, weights = hog_dflt_descriptor.detectMultiScale(\n",
" image, winStride=(8, 8), padding=(32, 32), scale=1.05,\n",
" finalThreshold=2, hitThreshold=1.0)\n",
"\n",
"for location, weight in zip(locations, weights):\n",
" x1, y1, w, h = location\n",
" x2, y2 = x1 + w, y1 + h\n",
" cv.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), thickness=3, lineType=cv.LINE_AA)\n",
" cv.putText(image, f\"{weight[0]:.2f}\", (x1,y1), cv.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2, cv.LINE_AA)\n",
"\n",
"plt.figure(figsize=(6,6))\n",
"plt.imshow(image[:,:,::-1]);"
]
},
{
"cell_type": "markdown",
"id": "6c8cf915",
"metadata": {},
"source": [
"# Segmentacja obrazu metodą GrabCut\n",
"\n",
"## Zadanie 1\n",
"\n",
"W poniższym zadaniu użyjemy algorytmu [GrabCut](https://en.wikipedia.org/wiki/GrabCut), będącego interaktywną metodą segmentacji obrazu, dzielącą obraz na pierwszy i drugi plan. W OpenCV algorytm jest zaimplementowany w funkcji [`cv.grabCut()`](https://docs.opencv.org/4.5.3/d3/d47/group__imgproc__segmentation.html#ga909c1dda50efcbeaa3ce126be862b37f). Dodatkowe informacje o algorytmie znajdują się w [dokumentacji](https://docs.opencv.org/4.5.3/d8/d83/tutorial_py_grabcut.html).\n",
"\n",
"Przygotuj interaktywną aplikację, która wykorzystuje algorytm GrabCut. W aplikacji powinna być możliwość zaznaczenia początkowego prostokąta, a następnie elementy maski (zwróć uwagę z jakich elementów może składać się maska). Przykładowe działanie możesz zaprezentować na obrazie `img/messi5.jpg`."
]
},
{
"cell_type": "markdown",
"id": "35f22bca",
"metadata": {},
"source": [
"![GrabCut - wynik](img/grabcut-result.png)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a633d931-adff-4321-b6f0-45422ffc99fe",
"metadata": {},
"outputs": [],
"source": [
"'''\n",
"===============================================================================\n",
"Interactive Image Segmentation using GrabCut algorithm.\n",
"\n",
"This application shows interactive image segmentation using grabcut algorithm.\n",
"\n",
"USAGE :\n",
" python grabcut.py <filename>\n",
"\n",
"README FIRST:\n",
" Two windows will show up, one for input and one for output.\n",
"\n",
" At first, in input window, draw a rectangle around the object using\n",
"mouse right button. Then press 'n' to segment the object (once or a few times)\n",
"For any finer touch-ups, you can press any of the keys below and draw lines on\n",
"the areas you want. Then again press 'n' for updating the output.\n",
"\n",
"Key '0' - To select areas of sure background\n",
"Key '1' - To select areas of sure foreground\n",
"Key '2' - To select areas of probable background\n",
"Key '3' - To select areas of probable foreground\n",
"\n",
"Key 'n' - To update the segmentation\n",
"Key 'r' - To reset the setup\n",
"Key 's' - To save the results\n",
"===============================================================================\n",
"'''\n",
"\n",
"import numpy as np\n",
"import cv2\n",
"import sys\n",
"\n",
"BLUE = [255,0,0] # rectangle color\n",
"RED = [0,0,255] # PR BG\n",
"GREEN = [0,255,0] # PR FG\n",
"BLACK = [0,0,0] # sure BG\n",
"WHITE = [255,255,255] # sure FG\n",
"\n",
"DRAW_BG = {'color' : BLACK, 'val' : 0}\n",
"DRAW_FG = {'color' : WHITE, 'val' : 1}\n",
"DRAW_PR_FG = {'color' : GREEN, 'val' : 3}\n",
"DRAW_PR_BG = {'color' : RED, 'val' : 2}\n",
"\n",
"# setting up flags\n",
"rect = (0,0,1,1)\n",
"drawing = False # flag for drawing curves\n",
"rectangle = False # flag for drawing rect\n",
"rect_over = False # flag to check if rect drawn\n",
"rect_or_mask = 100 # flag for selecting rect or mask mode\n",
"value = DRAW_FG # drawing initialized to FG\n",
"thickness = 3 # brush thickness\n",
"\n",
"def onmouse(event,x,y,flags,param):\n",
" global img,img2,drawing,value,mask,rectangle,rect,rect_or_mask,ix,iy,rect_over\n",
"\n",
" # Draw Rectangle\n",
" if event == cv2.EVENT_RBUTTONDOWN:\n",
" rectangle = True\n",
" ix,iy = x,y\n",
"\n",
" elif event == cv2.EVENT_MOUSEMOVE:\n",
" if rectangle == True:\n",
" img = img2.copy()\n",
" cv2.rectangle(img,(ix,iy),(x,y),BLUE,2)\n",
" rect = (ix,iy,abs(ix-x),abs(iy-y))\n",
" rect_or_mask = 0\n",
"\n",
" elif event == cv2.EVENT_RBUTTONUP:\n",
" rectangle = False\n",
" rect_over = True\n",
" cv2.rectangle(img,(ix,iy),(x,y),BLUE,2)\n",
" rect = (ix,iy,abs(ix-x),abs(iy-y))\n",
" rect_or_mask = 0\n",
" print (\" Now press the key 'n' a few times until no further change \\n\")\n",
"\n",
" # draw touchup curves\n",
" if event == cv2.EVENT_LBUTTONDOWN:\n",
" drawing = True\n",
" ix,iy = x,y\n",
" elif event == cv2.EVENT_MOUSEMOVE:\n",
" if drawing == True:\n",
" cv2.circle(img,(x,y),thickness,value['color'],-1)\n",
" cv2.circle(mask,(x,y),thickness,value['val'],-1)\n",
" elif event == cv2.EVENT_LBUTTONUP:\n",
" drawing = False\n",
" cv2.circle(img,(x,y),thickness,value['color'],-1)\n",
" cv2.circle(mask,(x,y),thickness,value['val'],-1)\n",
"\n",
"\n",
"img = cv2.imread('img/messi5.jpg')\n",
"img2 = img.copy() # a copy of original image\n",
"mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG\n",
"output = np.zeros(img.shape,np.uint8) # output image to be shown\n",
"\n",
"# input and output windows\n",
"cv2.namedWindow('output')\n",
"cv2.namedWindow('input')\n",
"cv2.setMouseCallback('input',onmouse)\n",
"cv2.moveWindow('input',img.shape[1]+10,90)\n",
"\n",
"print (\" Instructions : \\n\")\n",
"print (\" Draw a rectangle around the object using right mouse button \\n\")\n",
"\n",
"while(1):\n",
"\n",
" cv2.imshow('output',output)\n",
" cv2.imshow('input',img)\n",
" k = 0xFF & cv2.waitKey(1)\n",
"\n",
" # key bindings\n",
" if k == 27: # esc to exit\n",
" break\n",
" elif k == ord('0'): # BG drawing\n",
" print (\" mark background regions with left mouse button \\n\")\n",
" value = DRAW_BG\n",
" elif k == ord('1'): # FG drawing\n",
" print (\" mark foreground regions with left mouse button \\n\")\n",
" value = DRAW_FG\n",
" elif k == ord('2'): # PR_BG drawing\n",
" value = DRAW_PR_BG\n",
" elif k == ord('3'): # PR_FG drawing\n",
" value = DRAW_PR_FG\n",
" elif k == ord('s'): # save image\n",
" bar = np.zeros((img.shape[0],5,3),np.uint8)\n",
" res = np.hstack((img2,bar,img,bar,output))\n",
" cv2.imwrite('grabcut_output.png',output)\n",
" cv2.imwrite('grabcut_output_combined.png',res)\n",
" print (\" Result saved as image \\n\")\n",
" elif k == ord('r'): # reset everything\n",
" print (\"resetting \\n\")\n",
" rect = (0,0,1,1)\n",
" drawing = False\n",
" rectangle = False\n",
" rect_or_mask = 100\n",
" rect_over = False\n",
" value = DRAW_FG\n",
" img = img2.copy()\n",
" mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG\n",
" output = np.zeros(img.shape,np.uint8) # output image to be shown\n",
" elif k == ord('n'): # segment the image\n",
" print (\"\"\" For finer touchups, mark foreground and background after pressing keys 0-3\n",
" and again press 'n' \\n\"\"\")\n",
" if (rect_or_mask == 0): # grabcut with rect\n",
" bgdmodel = np.zeros((1,65),np.float64)\n",
" fgdmodel = np.zeros((1,65),np.float64)\n",
" cv2.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv2.GC_INIT_WITH_RECT)\n",
" rect_or_mask = 1\n",
" elif rect_or_mask == 1: # grabcut with mask\n",
" bgdmodel = np.zeros((1,65),np.float64)\n",
" fgdmodel = np.zeros((1,65),np.float64)\n",
" cv2.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv2.GC_INIT_WITH_MASK)\n",
"\n",
" mask2 = np.where((mask==1) + (mask==3),255,0).astype('uint8')\n",
" output = cv2.bitwise_and(img2,img2,mask=mask2)\n",
"\n",
"cv2.destroyAllWindows()"
]
}
],
"metadata": {
"author": "Andrzej Wójtowicz",
"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.9.2"
},
"subtitle": "06. Segmentacja i rozpoznawanie obrazów [laboratoria]",
"title": "Widzenie komputerowe",
"year": "2021"
},
"nbformat": 4,
"nbformat_minor": 5
}