Работаем с OpenCV на Python

OpenCV — известная библиотека машинного обучения и компьютерного зрения, имеющая открытый исходный код. Она включает в себя более 2500 алгоритмов, среди которых есть и классические, и современные. В принципе, OpenCV имеет интерфейсы на разных языках программирования (Java, C++ и Matlab), но сегодня мы поработаем с Python.

На установке долго останавливаться не будем, т. к. подробные инструкции есть здесь (для Windows) и здесь (для Linux).

Импорт и просмотр изображений

Импорт и просмотр изображений осуществляется следующим образом:

import cv2
image = cv2.imread("./путь/к/изображению.расширение")
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Замечание 1: во время чтения способом, который представлен выше, изображение находится не в RGB, а в BGR. Поначалу это, может, и неважно, но знать стоит. Тут есть несколько путей решения вопроса: 1. Поменяйте местами первый канал (R — красный) с третьим каналом (B — синий). В результате красный цвет будет не (255,0,0), а (0,0,255). 2. Поменяйте непосредственно цветовое пространство на RGB с помощью следующего кода:

rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

Замечание 2: если вы захотите закрыть окно, в котором изображение отображается, нажмите любую клавишу. Дело в том, что используя кнопку закрытия окна, можно столкнуться с подвисанием.

Идём дальше. На протяжении этой статьи мы будем применять для вывода изображений следующий код:

import cv2
def viewImage(image, name_of_window):
    cv2.namedWindow(name_of_window, cv2.WINDOW_NORMAL)
    cv2.imshow(name_of_window, image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Кадрирование

Посмотрите на эту картинку:

А теперь на то, как она будет выглядеть после кадрирования:

Для кадрирования:

import cv2
cropped = image[10:500, 500:2000]
viewImage(cropped, "Пёсик после кадрирования")

Здесь image[10:500, 500:2000] — это image[y:y + высота, x:x + ширина].

Меняем размер

Вот эта фотография довольно большая:

Давайте уменьшим её размер на 20 %:

import cv2
scale_percent = 20 # Это % от изначального размера
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
resized = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
viewImage(resized, "После изменения размера изображения на 20 %")

Обратите внимание, что данная функция учитывает соотношение сторон оригинального фото.

Поворот

Допустим, нам не нравится такой ракурс:

Давайте его перевернём на 180°:

import cv2
(h, w, d) = image.shape
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 180, 1.0)
rotated = cv2.warpAffine(image, M, (w, h))
viewImage(rotated, "Фото после поворота на 180°")

Что тут что: — image.shape возвратит высоту, ширину и каналы; — М — матрица поворота, поворачивающая фото на 180° относительно центра; — -ve — угол поворота по часовой стрелке; — +ve — угол поворота против часовой.

Переводим изображение в градации серого и в чёрно-белое отображение по порогу

Вот исходное фото:

Вот градации серого:

А вот чёрно-белый вариант:

```python import cv2 gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) ret, threshold_image = cv2.threshold(im, 127, 255, 0) viewImage(gray_image, "Градации серого") viewImage(threshold_image, "Чёрно-белый вариант")

В нашем случае gray_image является одноканальной версией изображения. Что касается функции threshold, то она возвращает изображение, где все пиксели, которые темнее (менее) 127 заменяются на 0, а все, которые больше (ярче) 127, заменяются на 255.

Ещё примерчик для ясности:
```python
ret, threshold = cv2.threshold(im, 150, 200, 10)

Тут всё, что темнее 150, меняется на 10, а всё, что ярче, меняется на 200.

Размытие/сглаживание

Вот отчётливое фото:

А вот оно же после размытия:

import cv2
blurred = cv2.GaussianBlur(image, (51, 51), 0)
viewImage(blurred, "Размытое изображение")

Здесь функция GaussianBlur осуществляет размытие по Гауссу, принимая три параметра: 1. Исходное фото. 2. Кортеж из двух нечётных положительных чисел (чем числа больше, тем, соответственно, больше сила сглаживания). 3. Параметры sigmaX и sigmaY. Если их оставить равными 0, значение параметров рассчитается автоматически.

Рисуем прямоугольники

Поместим лицо пёсика в прямоугольник:

import cv2
output = image.copy()
cv2.rectangle(output, (2600, 800), (4100, 2400), (0, 255, 255), 10)
viewImage(output, "Обводим прямоугольником")

Данная функция принимает пять параметров: 1. Непосредственно, изображение. 2. Координату левого верхнего угла (x1, y1). 3. Координату правого нижнего угла (x2, y2). 4. Цвет прямоугольника (GBR/RGB с учётом выбранной цветовой модели). 5. Толщину линии прямоугольника.

Рисуем линии

Вот два четвероногих друга:

Давайте разделим их прямой линией:

import cv2
output = image.copy()
cv2.line(output, (60, 20), (400, 200), (0, 0, 255), 5)
viewImage(output, "Два пса, разделённые линией")

Тут функция line тоже принимает пять параметров: 1. Непосредственно, само изображение, где линия рисуется. 2. Координату 1-й точки (x1, y1). 3. Координату 2-й точки (x2, y2). 4. Цвет линии (GBR/RGB с учётом выбранной цветовой модели). 5. Толщину линии.

Текст на изображении

Допустим, на изображение нужно нанести текст:

Сделаем это:

import cv2
output = image.copy()
cv2.putText(output, "We <3 Dogs", (1500, 3600),cv2.FONT_HERSHEY_SIMPLEX, 15, (30, 105, 210), 40) 
viewImage(output, "Изображение с текстом")

Здесь главную роль играет функция putText, принимающая аж семь параметров: 1. Изображение. 2. Текст для фото. 3. Координату левого нижнего угла начала текста (x, y). 4. Шрифт. 5. Размер шрифта. 6. Цвет текста (GBR/RGB с учётом выбранной цветовой модели). 7. Толщину линий букв.

Распознаём лица

Теперь обойдёмся без собак:

Итого: обнаружено 2 лица:

import cv2
image_path = "./путь/к/фото.расширение"
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
image = cv2.imread(image_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(
    gray,
    scaleFactor= 1.1,
    minNeighbors= 5,
    minSize=(10, 10)
)
faces_detected = "Лиц обнаружено: " + format(len(faces))
print(faces_detected)
# Рисуем квадраты вокруг лиц
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x+w, y+h), (255, 255, 0), 2)
viewImage(image,faces_detected)

Тут используется общая функция распознавания лиц и объектов detectMultiScale. Чтобы она искала именно лица, передаём ей соответствующий каскад.

Функция принимает четыре параметра: 1. Обрабатываемое фото в градации серого. 2. Параметр scaleFactor. Параметр компенсирует перспективу, ведь одни лица бывают больше, т. к. находятся ближе. 3. Параметр minNeighbors, определяющий число объектов вокруг лица. Вообще, во время распознавания объектов соответствующий алгоритм использует скользящее окно. И чем больше значение вышеназванного параметра, тем больше аналогичных объектов нужно алгоритму, чтобы он определил, что текущий объект — это лицо. Если значение будет слишком маленьким, повысится число ложных срабатываний, если слишком большим, алгоритм станет более требовательным. 4. Последний параметр — minSize отвечает за размер этих областей.

Сохранение изображения

Когда нужные манипуляции произведены, можем сохранить изображение:

import cv2
image = cv2.imread("./импорт/путь.расширение")
cv2.imwrite("./экспорт/путь.расширение", image)

Вывод

OpenCV — прекрасная библиотека с простыми алгоритмами. Она пригодится вам при продвинутом редактировании изображений, 3D-рендере, идентификации и отслеживании людей и объектов на видео, поиске идентичных фото из определённого набора и т. д. Пожалуй, правы те, кто говорит, что нельзя переоценить важность OpenCV для людей, разрабатывающих ML-проекты в области изображений и не только.

Источник