Всем привет. Сегодня мы потыкаем opencv на нашем любимом питончике.
Весь код есть на гитхабе
Для начала надо бы установить все для работы. Не знаю как Вы, а я юзаю PyCharm, в котором при создании проекта создается и virtualenv. Если Вы Ъ хацкер, который живет в терминале, Вам нужно будет создать виртуальную среду собственными руками. Это не так сложно, но описывать это я, конечно, не буду)
После того, как мы со всем разобрались, устанавливаем opencv:
$ pip3 install opencv-python
Все. Настройка завершена. Теперь засунем в проект какую нибудь картинку. (Милк занят косточкой, поэтому воспользуемся тырнетами). Картинку положим рядом с нашим .py файлом, а в самом файле напишем следующее:
%lang(python)% import cv2 as cv image = cv.imread("./image.jpg") cv.imshow("Dog & cat", image) cv.waitKey(0) cv.destroyAllWindows()
И нажмем Run
Если Вы все сделали правильно вы должны будете увидеть Вашу картинку:
Чтобы закрыть окошко нужно нажать любую клавишу.
Окей, уже что-то есть, идем дальше.
Следует отметить, что в загруженной картинке цвет кодируется не RGB, а BGR.
Вот так мы можем получить нашу картинку в разных цветовых пространствах:
%lang(python)% rgb = cv.cvtColor(image, cv.COLOR_BGR2RGB) #rgb hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) #hsv gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) #50 оттенков серого
Тут можно почитать подробнее о Color conversions
Если Вы выбрали большую картинку, то наверное заметили неудобство - картинка открывается в реальном размере и не ограничивается даже размером монитора.
Сейчас мы это пофиксим:
%lang(python)% import cv2 as cv scale = 0.5 image = cv.imread("./image.jpg") width = int(image.shape[1] * scale) height = int(image.shape[0] * scale) resized = cv.resize(image, (width, height), interpolation=cv.INTER_AREA) cv.imshow("Dog & cat resized", resized) cv.waitKey(0) cv.destroyAllWindows()
Мы уменьшили картинку в 2 раза:
Очень часто, например, в задачах слежения за объектами гораздо проще работать с картинкой, на которой будет только 2 цвета и сейчас мы научимся это делать:
%lang(python)% import cv2 as cv image = cv.imread("./image.jpg") gray_image = cv.cvtColor(image, cv.COLOR_BGR2GRAY) ret, threshold_image = cv.threshold(gray_image, 127, 255, cv.THRESH_BINARY) cv.imshow("Dog & cat wb", threshold_image) cv.waitKey(0) cv.destroyAllWindows()
Что мы сделали? Сначала мы перевели картинку в градации серого:
Цвет у нас меняется как бы от 0 до 255
И мы говорим: все, что светлее, чем 127 пусть будет чисто белое (255), а все, что темнее - чисто черное.
И получили это:
Тут можно почитать подробнее про thresholding
Что же еще можно сделать? На самом деле много всего, но сейчас мне в голову пришло размытие по Гауссу, пробуем:
%lang(python)% import cv2 as cv image = cv.imread("./guys.jpg") blur = cv.GaussianBlur(image, (63, 63), cv.BORDER_DEFAULT) cv.imshow("Russian gopniks", blur) cv.waitKey(0) cv.destroyAllWindows()
Вторым аргументом cv.GaussianBlur принимает кортеж - так называемый Gaussian Kernel Size.
Опытным путем было установлено, что чем больше число, тем сильнее размытие. При этом (50, 50) выкинул мне ошибку.
Последним аргументом идет borderType он определяет границу изображения. Но последний он у меня, а функция может принимать еще два аргумента: sigmaX и sigmaY. Это стандартное отклонение ядра по оси X и Y соответственно.
Вот что мы получили:
чОткие ровные пацанчики с района больше не чОткие :(
Теперь предлагаю попробовать сложную геометрическую трансформацию - Affine Transformation.
Для этого нам, кстати, понадобится еще и Numpy. К счастью, он появился у нас вместе с opencv.
%lang(python)% import numpy as np import cv2 as cv image = cv.imread('./tower.jpg') rows, cols, ch = image.shape pts1 = np.float32([[50, 50], [200, 50], [50, 200]]) pts2 = np.float32([[10, 100], [200, 50], [100, 250]]) M = cv.getAffineTransform(pts1, pts2) affine = cv.warpAffine(image, M, (cols, rows)) cv.imshow("Tower", affine) cv.waitKey(0) cv.destroyAllWindows()
pts я взял из доков
По сути мы задаем положение каких то точек, а потом новое положение этих точек, так мы получаем Affine Transform.
Как итог - картинка поворачивается.
Но все это блин как то скучно. Давайте уже попробуем что-нибудь распознать. Например, лицо)
Потребуется скачать это
%lang(python)% import cv2 as cv face_cascade = cv.CascadeClassifier('./haarcascade_frontalface_default.xml') image = cv.imread('face.jpg') gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x, y, w, h) in faces: cv.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2) roi_gray = gray[y:y+h, x:x+w] roi_color = image[y:y+h, x:x+w] cv.imshow('Detect', image) cv.waitKey(0) cv.destroyAllWindows()
Результат:
Ура! Работает.
Давайте попробуем обвести еще и глаза.
Для этого нам понадобится еще это
%lang(python)% import cv2 as cv face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml') eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml') image = cv.imread('face.jpg') gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: cv.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2) roi_gray = gray[y:y+h, x:x+w] roi_color = image[y:y+h, x:x+w] eyes = eye_cascade.detectMultiScale(roi_gray) for (ex, ey, ew, eh) in eyes: cv.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2) cv.imshow('Detect', image) cv.waitKey(0) cv.destroyAllWindows()
Результат:
Здорово, правда?
В завершение статьи давайте соберем все в кучу и попробуем детектить лицо на видео. Делается это так же просто:
%lang(python)% import cv2 as cv cap = cv.VideoCapture(0) face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml') fourcc = cv.VideoWriter_fourcc(*'XVID') out = cv.VideoWriter('me.avi', fourcc, 20.0, (640, 480)) while True: ret, frame = cap.read() gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x, y, w, h) in faces: cv.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) cv.imshow('frame', frame) out.write(frame) if cv.waitKey(1) & 0xFF == ord('q'): break out.release() cap.release()
Тут, кстати, еще я пишу видео, чтобы потом конвертнуть в гифку и вставить сюда: