YOLO– это эффективный алгоритмический метод выделения объектов изображения. Мы будем использовать его для написания программы на языке Python, чтобы вычислить число людей в комнате, и попытаемся разгадать капчу. И тут же научимся как обнаруживать обьекты на изображениях.
Распознавание объектов сегодня полезно для решения самых разных задач: классификации видов растений и животных, распознавания лиц, определения размеров объектов — и это далеко не полный список.
Виды алгоритмов
Существует множество алгоритмов обнаружения объектов, которые были сделаны на изображениях и видео. А что они собой представляют сейчас будем разбирать.
Fast R-CNN, Fast Region-Based Convolutional Neural Network
Подход аналогичен алгоритму R-CNN. Но вместо предварительного выделения областей мы передаем входное изображение в CNN для создания сверточной карты ресурсов, где будет выполняться выборочный поиск, а специальный слой Softmax выполняет прогнозирование класса ресурсов.
Обработка в реальном времени: не поддерживается.
YOLO, You Only Look Once
Изображение разделено на квадратную сетку. Для каждой ячейки в сети CNN отображает вероятности присвоенного класса. Ячейки с вероятностью класса выше порога выбираются и используются для определения местоположения объекта на изображении.
Обработка в реальном времени: поддерживается!
R-CNN, Region-Based Convolutional Neural Network
Сначала на изображении с помощью алгоритма выборочного поиска выбираются области, которые должны содержать объект. Затем сверточная нейронная сеть (CNN) пытается идентифицировать характеристики объектов для каждой из этих областей, после чего вспомогательная векторная машина классифицирует полученные данные и сообщает класс обнаруженного объекта
.Обработка в реальном времени: не поддерживается.
Faster R-CNN, Faster Region-Based Convolutional Neural Network
Как и Fast R-CNN, изображение передается в CNN для создания сверточной карты симптомов, но вместо алгоритма выборочной проверки используется отдельная сеть для прогнозирования предложений по регионам.
Обработка в реальном времени: поддерживается при высокой вычислительной мощности.
Как видите, YOLO по-прежнему является лучшим выбором для обнаружения и распознавания образов. Он отличается высокой скоростью и точностью обнаружения объектов, и этот алгоритм также можно использовать в проектах на Android и Raspberry Pi с использованием нетребовательной крошечной версии сети, с которой мы будем работать с вами сегодня.
Версия Tiny немного проигрывает именно полноценной сетевой версии, но также требует меньших вычислительных мощностей, что позволит нам запустить проект, который мы будем делать сегодня, как на слабом компьютере, так и, при необходимости, на смартфоне.
Код
Чтобы написать легковесное приложение для обнаружения объектов на изображении, нам с тобой понадобятся:
- названия классов датасета COCO, которые умеет определять сеть YOLO;
- конфигурации для tiny-варианта сети YOLO;
- веса для tiny-варианта сети YOLO.
Дополнительно установим библиотеки OpenCV и NumPy:
pip install opencv-python
pip install numpy
Теперь напишем приложение, которое будет находить объекты на изображении при помощи YOLO и отмечать их.
Мы попытаемся обойти изображения грузовиков CAPTCHA — класс truck в наборе данных COCO. Кроме того, мы посчитаем количество обнаруженных объектов нужного нам класса и выведем всю информацию на экран.
Начнем с написания функции для использования YOLO. С ее помощью будут определены наиболее вероятные классы объектов на картинке, а также координаты их границ, которые мы позже будем использовать для рисования.
import cv2
import numpy as np
def apply_yolo_object_detection(image_to_process):
"""
Распознавание и определение координат объектов на изображении
:param image_to_process: исходное изображение
:return: изображение с размеченными объектами и подписями к ним
"""
height, width, depth = image_to_process.shape
blob = cv2.dnn.blobFromImage(image_to_process, 1 / 255, (608, 608), (0, 0, 0), swapRB=True, crop=False)
net.setInput(blob)
outs = net.forward(out_layers)
class_indexes, class_scores, boxes = ([] for i in range(3))
objects_count = 0
# Запуск поиска объектов на изображении
for out in outs:
for obj in out:
scores = obj[5:]
class_index = np.argmax(scores)
class_score = scores[class_index]
if class_score > 0:
center_x = int(obj[0] * width)
center_y = int(obj[1] * height)
obj_width = int(obj[2] * width)
obj_height = int(obj[3] * height)
box = [center_x - obj_width // 2, center_y - obj_height // 2, obj_width, obj_height]
boxes.append(box)
class_indexes.append(class_index)
class_scores.append(float(class_score))
# Выборка
chosen_boxes = cv2.dnn.NMSBoxes(boxes, class_scores, 0.0, 0.4)
for box_index in chosen_boxes:
box_index = box_index[0]
box = boxes[box_index]
class_index = class_indexes[box_index]
# Для отладки рисуем объекты, входящие в искомые классы
if classes[class_index] in classes_to_look_for:
objects_count += 1
image_to_process = draw_object_bounding_box(image_to_process, class_index, box)
final_image = draw_object_count(image_to_process, objects_count)
return final_image
Далее добавим функцию, которая позволит нам обвести найденные на изображении объекты с помощью координат границ, которые мы получили в apply_yolo_object_detection
.
def draw_object_bounding_box(image_to_process, index, box):
"""
Рисование границ объекта с подписями
:param image_to_process: исходное изображение
:param index: индекс определенного с помощью YOLO класса объекта
:param box: координаты области вокруг объекта
:return: изображение с отмеченными объектами
"""
x, y, w, h = box
start = (x, y)
end = (x + w, y + h)
color = (0, 255, 0)
width = 2
final_image = cv2.rectangle(image_to_process, start, end, color, width)
start = (x, y - 10)
font_size = 1
font = cv2.FONT_HERSHEY_SIMPLEX
width = 2
text = classes[index]
final_image = cv2.putText(final_image, text, start, font, font_size, color, width, cv2.LINE_AA)
return final_image
Добавим функцию, которая выведет количество распознанных объектов на изображении.
def draw_object_count(image_to_process, objects_count):
"""
Подпись количества найденных объектов на изображении
:param image_to_process: исходное изображение
:param objects_count: количество объектов искомого класса
:return: изображение с подписанным количеством найденных объектов
"""
start = (45, 150)
font_size = 1.5
font = cv2.FONT_HERSHEY_SIMPLEX
width = 3
text = "Objects found: " + str(objects_count)
# Вывод текста с обводкой (чтобы было видно при разном освещении картинки)
white_color = (255, 255, 255)
black_outline_color = (0, 0, 0)
final_image = cv2.putText(image_to_process, text, start, font, font_size, black_outline_color, width * 3, cv2.LINE_AA)
final_image = cv2.putText(final_image, text, start, font, font_size, white_color, width, cv2.LINE_AA)
return final_image
Напишем функцию, которая будет анализировать изображение и выводить на экран результат работы написанных нами алгоритмов.
def start_image_object_detection():
"""
Анализ изображения
"""
try:
# Применение методов распознавания объектов на изображении от YOLO
image = cv2.imread("assets/truck_captcha.png")
image = apply_yolo_object_detection(image)
# Вывод обработанного изображения на экран
cv2.imshow("Image", image)
if cv2.waitKey(0):
cv2.destroyAllWindows()
except KeyboardInterrupt:
pass
А теперь мы создадим функцию main
, в которой настроим нашу сеть и попробуем запустить ее.
if __name__ == '__main__':
# Загрузка весов YOLO из файлов и настройка сети
net = cv2.dnn.readNetFromDarknet("yolov4-tiny.cfg", "yolov4-tiny.weights")
layer_names = net.getLayerNames()
out_layers_indexes = net.getUnconnectedOutLayers()
out_layers = [layer_names[index[0] - 1] for index in out_layers_indexes]
# Загрузка из файла классов объектов, которые умеет обнаруживать YOLO
with open("coco.names.txt") as file:
classes = file.read().split("\n")
# Определение классов, которые будут приоритетными для поиска на изображении
# Названия находятся в файле coco.names.txt
# В данном случае определяется грузовик для прохождения CAPTCHA
classes_to_look_for = ["truck"]
start_image_object_detection()
Давай посмотрим, как алгоритм YOLO справился с тестом простой CAPTCHA.
Исходная CAPTCHA
Результат применения YOLO
Некоторая погрешность все же есть, но два из трех грузовиков алгоритм выбрал правильно.
Модификация приложения
Теперь мы с вами приступим к решению практической задачи, в которой нам будет важно контролировать количество людей в комнате. Более того, во время ограничительных мер, связанных с COVID-19, это не только интересно, но и актуально.
Чтобы сделать задачу «живым примером», воспользуемся общественной камерой, установленной в одной из парикмахерских Лондона. Благодаря небольшому пространству внутри может находиться не более десяти человек.
Для решения этой проблемы достаточно добавить функцию, которая будет обрабатывать видео кадр за кадром и отображать результат обработки на экране. Чтобы не загружать устройство обработкой каждого кадра, экран будет обновляться при нажатии любой клавиши. В этом нет необходимости на мощных компьютерах.
def start_video_object_detection():
"""
Захват и анализ видео в режиме реального времени
"""
while True:
try:
# Захват картинки с видео
video_camera_capture = cv2.VideoCapture("http://81.130.136.82:82/mjpg/video.mjpg")
while video_camera_capture.isOpened():
ret, frame = video_camera_capture.read()
if not ret:
break
# Применение методов распознавания объектов на кадре видео от YOLO
frame = apply_yolo_object_detection(frame)
# Вывод обработанного изображения на экран с уменьшением размера окна
frame = cv2.resize(frame, (1920 // 2, 1080 // 2))
cv2.imshow("Video Capture", frame)
if cv2.waitKey(0):
break
video_camera_capture.release()
cv2.destroyAllWindows()
except KeyboardInterrupt:
pass
Также нам потребуется немного модифицировать функцию main
, чтобы теперь запускать обработку видео вместо обработки изображения.
if __name__ == '__main__':
# Загрузка весов YOLO из файлов и настройка сети
net = cv2.dnn.readNetFromDarknet("yolov4-tiny.cfg", "yolov4-tiny.weights")
layer_names = net.getLayerNames()
out_layers_indexes = net.getUnconnectedOutLayers()
out_layers = [layer_names[index[0] - 1] for index in out_layers_indexes]
# Загрузка из файла классов объектов, которые умеет обнаруживать YOLO
with open("coco.names.txt") as file:
classes = file.read().split("\n")
# Определение классов, которые будут приоритетными для поиска на изображении
# Названия находятся в файле coco.names.txt
# В данном случае определяется грузовик для прохождения CAPTCHA и человек для анализа видео
classes_to_look_for = ["truck", "person"]
start_video_object_detection()
Получаем результат: шесть из семи человек были распознаны.
Результат обработки видео
Также можно добавить и другие полезные функции: например, отправлять на почту или в Telegram сообщение о том, что в барбершоп собралось много народу.
Итоги
Алгоритмы обнаружения объектов не дают стопроцентной точности, но они по-прежнему эффективны и могут работать намного быстрее, чем любой из нас.
Вы можете спросить себя, почему мы не соблюдаем расстояние в полтора метра. В реальной жизни проверить это будет сложно: например, когда у вас есть пара друзей, семья с ребенком или действие разворачивается невозможно без тесного контакта, а в парикмахерской это происходит постоянно. Кроме того, если камера расположена под неправильным углом, возрастает сложность измерения расстояния.
Для этого потребуются алгоритмы, которые в будущем будут определять размеры объектов и будут работать с трехмерным пространством. Они используются для обнаружения транспортных средств в беспилотных автомобилях — Aggregate View Object Detection или YOLO 3D Oriented Object Bounding Box Detection.