[Практика 5] opencv play
Фев 18, 2020 в 01:22  •  23 мин  •  читали 811 раз

Всем привет. Сегодня мы потыкаем 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.

Как итог - картинка поворачивается.


Но все это блин как то скучно. Давайте уже попробуем что-нибудь распознать. Например, лицо)


Потребуется скачать это


Почитать про Haar Cascades


%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()


Тут, кстати, еще я пишу видео, чтобы потом конвертнуть в гифку и вставить сюда: