Detekcja ruchu za pomocą skryptu w języku Python oraz biblioteki OpenCV
Biblioteka OpenCV pozwala na zaawansowane przetwarzanie obrazów przy użyciu języków C++, Python i Java. Jak wykonać przydatną operację porównania dwóch obrazów (co w konsekwencji pozwoli na wykrycie ruchu)? — zobaczymy na praktycznym przykładzie skryptu w języku Python.
Kolejne kroki algorytmu będą wyglądały jak następuje:
- wczytamy dwa wybrane obrazy
- nałożymy maskę na wybrany prostokątny obszar (bo zazwyczaj interesujący jest tylko fragment całego obrazu)
- skonwertujemy obraz do skali szarości
- obliczymy współczynnik SSIM (liczbową wartość określającą względną różnicę między obrazami)
- oznaczymy kontury zmienionych obszarów
- wygenerujemy mapę zmienionych obszarów
- stworzymy mapę zmienionych obszarów przy zastosowaniu progowania (białym kolorem oznaczone będą obszary zmienione)
Otrzymany rezultat warto poddać dalszemu przetwarzaniu: sprawdzić, gdzie dokładnie leży liczbowa granica pomiędzy brakiem ruchu a ruchem, albowiem nie każda dodatnia wartość oznacza faktyczny ruch (niskie wartości mogą oznaczać niewielkie zmiany w obrazie, np. ruch liści na drzewie).
{importujemy niezbędne pakiety}
from skimage.measure import compare_ssim
import argparse
import imutils
import cv2
import numpy as npap = argparse.ArgumentParser()
ap.add_argument(„-f”, „–first”, required=True,
help=”first input image”)
ap.add_argument(„-s”, „–second”, required=True,
help=”second”)
args = vars(ap.parse_args()){wczytujemy dwa obrazki, które będą porównywane ze sobą}
imageA = cv2.imread(args[„first”])
imageB = cv2.imread(args[„second”]){operujemy na masce…}
mask = np.zeros(imageA.shape, dtype=np.uint8)
roi_corners = np.array([[{współrzędne wybranego prostokątnego obszaru, np. (x1,y1),(x2,y2),(x3,y3),(x4,y4)}]], dtype=np.int32)
channel_count = imageA.shape[2]
ignore_mask_color = (255,)*channel_count
cv2.fillPoly(mask, roi_corners, ignore_mask_color)
imageA = cv2.bitwise_and(imageA, mask)
{… tutaj kończymy operację na masce}{kod tak samo jak wyżej, ale tym razem dla obrazka imageB}
{konwertujemy obrazy do skali szarości}
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY){obliczamy wartość SSIM}
(score, diff) = compare_ssim(grayA, grayB, full=True)
diff = (diff * 255).astype(„uint8”){otrzymujemy wynik, tutaj wyrażony jako dopełnienie do wartości 10000}
print( int( round(1e4*(1-score)) ) )
Wygenerowanie tablicy konturów wskazujących zmienione obszary wygląda wtedy następująco:
thresh = cv2.threshold(diff, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
W środowisku systemów linuksowych z rodziny Debian uruchomienie skryptu po uprzedniej instalacji pakietów python3, python-opencv oraz pip wraz z bibliotekami libsm6, libxext6 oraz aktualizacji pakietów Pythona scikit-image i imutils odbywa się według schematu:
python3 c.py -f a.jpg -s b.jpg
Czas wykonania skryptu Pythona wykrywającego ruch według powyższego przepisu to około 1,5 sekundy (przy użyciu pojedynczego procesora Intel Xeon 2,2 GHz).
Przydatne pozycje książkowe:
A. Pajankar ’Raspberry Pi Computer Vision Programming’ (2015)
J. Minichino, J. Howse ’Learning OpenCV 3 Computer Vision with Python’ (2015)
Wzorcowa implementacja:
Image Difference with OpenCV and Python
https://www.pyimagesearch.com/2017/06/19/image-difference-with-opencv-and-python/
Definicja miary SSIM w Wikipedii:
https://en.wikipedia.org/wiki/Structural_similarity
Oficjalna witryna biblioteki OpenCV:
https://opencv.org/