Привет! Меня зовут Влад Губайдулин, я full-stack разработчик в департаменте Логистика КОРУС Консалтинг. Весной на Хабре я рассказывал о том, как для дипломного проекта создавал приложение для отслеживания объектов спортивного мероприятия. Почитать об этом можно вот здесь.

Из моего пет-проекта вырос полноценный реальный проект. Знания и навыки в области нейронных сетей, трекинговых библиотек и компьютерного зрения, которые я приобрел, были использованы для разработки системы отслеживания транспортных средств на производственных территориях. Эта система основана на применении сверточной нейронной сети — технологии, позволяющей компьютерам «видеть» и интерпретировать визуальную информацию

Наш департамент разрабатывает и внедряет системы и ПО для логистики, поэтому развитие проекта было вполне закономерным. Но оно не случилось бы без участия коллег, которые вдохновили меня на эту идею, за что им спасибо.
Сегодня расскажу, в чем суть этой системы, остановлюсь на принципах работы, инструментах и архитектуре.

Определяем цель

Я рассматриваю наше приложение как дополнение к существующим системам управления двором (YMS) и как способ более интеллектуального отслеживания транспортных средств на территории промышленных зон. 

В современных системах трекинг этапов перемещения транспортных средств (въезд на территорию/разгрузка/погрузка и так далее) в основном осуществляется с помощью вторичных инструментов: камер для чтения номеров, датчиков парковки и карточек. Меня же интересует реализация подобной функции исключительно за счет обыкновенных камер наблюдения без дополнительных «железок» — это упростит и автоматизирует процесс. 

Представьте систему видеонаблюдения, в которой отслеживание осуществляется с нескольких камер, каждая из которых имеет собственную трекинг-метку. Когда транспортное средство попадает в поле зрения камеры, она присваивает ему уникальный идентификатор. ТС перемещается и попадает в зону видимости другой камеры —  новая камера также присваивает ID. Еще есть адаптер, который сопоставляет эти идентификационные номера при выезде ТС из одного кадра и появлении его в следующем по логике кадре. На основе этой логики формируются импровизированные контрольные точки.

Камера 1
Камера 1
Камера 2
Камера 2

В качестве преимуществ такого приложения — помимо собственно автоматизации — выделю несколько функций: 

  1. Анализ фактического нахождения транспортного средства на каждом этапе разгрузки. Поможет поддерживать и оптимизировать производительность  загрузочно-разгрузочных пунктов.

  2. Мгновенная реакция в случае, если транспортное средство оказалось в запретной зоне.

  3. Учет срока пребывания ТС вне парковочного места или дока разгрузки — если машина долго стоит неподвижно, система уведомит о возможной поломке. 

  4. Идентификация свободного парковочного места или дока разгрузки — следующее транспортное средство можно вызвать на место автоматически..

  5. Расчет скорости движения транспортного средства: при превышении допустимой скорости можно провести беседу с водителем при выезде.

Подобных готовых решений на рынке я пока не нашел ни у российских, ни у зарубежных поставщиков — встречаются лишь исследовательские работы по этой теме. Но если я что-то упустил и у вас есть информация о подобных разработках, пожалуйста, делитесь в комментариях.

Раскладываем инструменты

Тут мы делаем DataScience, поэтому, разумеется, Python — наш лучший друг.

Для хранения данных используем SQLite, простая и легкая база данных.

Чтобы не заморачиваться с запросами к базе, а сразу работать с нужными объектами, возьмем SQLAlchemy.

Теперь перейдем к более интересной части — отслеживанию объектов в видео. Здесь на помощь приходят две мощные библиотеки: ByteTrack и YOLOv8.

ByteTrack — это инструмент, который помогает следить за несколькими объектами в видео. Делает это легко и эффективно, даже если объекты частично скрыты или находятся далеко от камеры.

YOLOv8  — это одна из самых современных моделей для обнаружения объектов, известна своей скоростью и точностью, что делает её идеальной для задач, выполняемых в режиме реального времени, таких как видеонаблюдение или автономное вождение. 

И, конечно, библиотека OpenCV — это отличный инструмент для работы с изображениями и видео. Она позволяет делать практически всё: от простого чтения и записи видео до сложных операций, таких как распознавание лиц или фильтрация изображений.

С ее помощью вы можете изменять размер, поворачивать, улучшать качество изображения, определять контуры объектов.  И все это даже иногда работает.

Конвейер обработки видеоизображения представлен в листинге 1. В нем можно найти пример подключения YOLO и ByteTrack, а также отрисовку с помощью OpenCV.

Листинг 1

async def main(self, is_show_frames):
   device = 'cuda' if torch.cuda.is_available() else 'cpu'
   print(f'Using device: {device}')
   model = YOLO('yolov8l.pt').to(device)
 
   cap = cv2.VideoCapture(self.VIDEO_PATH)
 
   ret, frame = cap.read()
 
   byte_tracker = BYTETracker(BYTETrackerArgs.BYTETrackerArgs())
 
   while ret:
       results = model(frame)[0]
       detections = Detection.Detection.from_results(
           pred=results.boxes.data.cpu().numpy(),
           names=model.names)
 
       car_detections = filter_detections_by_class(detections=detections, class_name="car")
       truck_detections = filter_detections_by_class(detections=detections, class_name="truck")
       detections = car_detections + truck_detections
 
       tracks = byte_tracker.update(
           output_results=detections2boxes(detections=detections),
           img_info=frame.shape,
           img_size=frame.shape
       )
 
       tracked_detections = match_detections_with_tracks(detections=detections, tracks=tracks)
 
       for detection in tracked_detections:
           tracked_objects_aud_id = await Recording().record_tracks_async(detection, self.CAMERA, self.AREA_EXIT)
 
           if is_show_frames:
               cv2.rectangle(frame,
                             (int(detection.rect.x), int(detection.rect.y)),
                             (int(detection.rect.x + detection.rect.width), int(detection.rect.y + detection.rect.height)),
                             (255, 255, 255), 2)
 
               cv2.putText(frame,
                           f'{detection.class_name}_id#{tracked_objects_aud_id}',
                           (int(detection.rect.x), int(detection.rect.y) - 10),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)
 
       if is_show_frames:
           if self.IS_PARKING_EXIST:
               parking_color = self.COLOR.get_current_parking_color(tracked_detections, self.AREA_PARKING_PLACE)
 
               # add bounding box for parking
               cv2.rectangle(frame,
                             self.AREA_PARKING_PLACE[0],
                             self.AREA_PARKING_PLACE[1],
                             parking_color, 2)
               cv2.putText(frame,
                           f'parking_place',
                           self.TEXT_PARKING_PLACE,
                           cv2.FONT_HERSHEY_SIMPLEX, 0.6, parking_color, 2)
 
           # add bounding box for exit
           cv2.rectangle(frame,
                         self.AREA_EXIT[0],
                         self.AREA_EXIT[1],
                         (0, 0, 150), 2)
           cv2.putText(frame,
                       f'exit_place',
                       self.TEXT_EXIT,
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 128), 2)
 
           cv2.namedWindow('frame')
           cv2.imshow('frame', frame)
 
       k = cv2.waitKey(33)
       if k == 27:
           break
 
       ret, frame = cap.read()
 
   cap.release()
   cv2.destroyAllWindows()

Определяем архитектуру

Архитектура нашего приложения состоит из двух ключевых компонентов: клиентской и серверной части. Клиентская часть занимается отправкой видеофреймов на удалённый сервер, для чего использует брокер сообщений для передачи данных в виде байтов. Серверная часть берет на себя обработку этих фреймов, извлекает необходимую информацию и сохраняет ее в базу данных. После этого агрегированные данные могут быть отображены в пользовательском интерфейсе.

На производственной площадке установлены IP-камеры, которые контролируют парковочные зоны и зоны загрузки. Каждая из камер передаёт фреймы на сервер для последующей обработки. На сервере применяется алгоритм, использующий нейронные сети для выделения и анализа интересующих объектов.

  • Новый объект: Если система обнаруживает объект с новым идентификатором (ID), алгоритм пытается сопоставить его с транспортным средством, которое имеет схожее предыдущее местоположение и последнюю запись в пределах допустимого временного интервала.

  • Старый объект: Если объект уже имеет существующий ID, система просто обновляет его местоположение.

Для переиндексации объектов используется таблица exit_zones, содержащая информацию о присутствии объектов в ассоциативных границах камер. Эта таблица включает ID последнего транспортного средства в границе, ID камеры с общей границей и статус наличия транспортного средства в данной зоне.

Например, если объект покидает зону видимости Камеры 1 и попадает в зону видимости Камеры 2, данные с Камеры 2 обрабатываются для определения, является ли этот объект новым для неё. Если да, система проверяет, отслеживался ли он другими камерами. В случае положительного результата происходит переиндексация. Если нет — объекту присваивается новый индекс. Для старых объектов просто обновляется информация о местоположении.

Все данные передаются на интерфейс и доступны для просмотра в хранилище. К базе данных можно подключать другие системы через API или брокер сообщений.

 Функциональные возможности системы

Функционально система делится на две части: 

  1. Ведение журнала о перемещении объектов на видео; 

  2. Комплексное отслеживание объектов на территории по нескольким камерам с учетом перехода объектов из области видимости одной камеры в другую.

Первая часть на данный момент реализована целиком. Спроектирована UML диаграмма классов, которая легла в основу схемы базы данных. Для минимальной работы системы необходимо три класса: 

  • камеры (cameras); 

  • отслеживаемые объекты (tracked_objects_aud); 

  • ассоциативные границы (exit_zones). Класс ассоциативных границ необходим для переиндексации объектов при переходе с одной камеры на другую. 

Вторая часть — собственно отслеживание объектов — реализована частично. Уже готов алгоритм, который отвечает за переиндексацию отслеживаемых объектов при переходе объекта с одной камеры на другую. Он вызывается на каждом обрабатываемом кадре для любого обнаруженного объекта после записи нового транспортного средства или актуализации данных старого в базе данных.

Осталось доработать момент с расчетом идентичности двух обнаруженных объектов и принятием решения об индексации. То есть, сейчас алгоритм довольно‑таки глуп: при пересечении границ камер он любой машине присвоит старый индекс, а суть конечной идеи, чтобы он точно мог определить, что в зону видимости заехало новое транспортное средство или, что мы продолжаем наблюдать за старым.

Сейчас думаю о том, чтобы сделать собственную нейросеть, которая будет сверять по свертке два объекта и выдавать % соотношения их идентичности. Пока все на уровне эксперимента.  

Такая система может быть запущена на любом производстве. Достаточно только вручную настроить ассоциативные зоны-границы камер — остальное она определит сама. Ахиллесова пята решения — производительность, так как для нормальной работы системы из коробки нужны мощные ресурсы: цп, видеокарта, оперативка. В качестве компромисса можно рассмотреть ограничения: обрабатывать каждый n-ый кадр, вместо того, чтобы обрабатывать каждый. При таком подходе нужно будет рассчитать, сколько кадров можно пропустить.

Псевдокод

Метод, ответственный за переиндексацию отслеживаемых объектов при переходе объекта с одной камеры на другую, вызывается на каждом обрабатываемом кадре для любого обнаруженного объекта после записи нового транспортного средства или актуализации данных старого в базе данных. Алгоритм переиндексации представлен псевдокодом А1.

Алгоритм А1

Procedure actualize_exit_zones_data
  Ввод exit_area_rect – координаты выделяющей рамки ассоциативной границы
Ввод transport_rect – координаты выделяющей рамки транспортного средства
Ввод camera_id – идентификационный номер камеры, с которой пришёл кадр
If exit_area_rect и transport_rect пересекаются then
            Получение из БД текущее состояние модели ассоциативной границы из класса exit_zone
            If значение tracked_objects_aud_id НЕ равно текущему транспортному средству then
                    Присвоение tracked_objects_aud_id значение id текущего транспортного средства
                   Присвоение флагу is_empty значение 0
            End
   End
  Else if значение tracked_objects_aud_id для ассоциативной границы из класса exit_zone равен текущему транспортному средству then
             Присвоение флагу is_empty значение 1
             Получение пар id объектов и id ассоциативной границ, которые связаны, при условии, что связанная граница также имеет флаг is_empty равный 1
             If такая пара ассоциативных границ существует then
                    Перезапись координат и камеры в более старой модели для tracked_objects_aud на актуальные, новый id удаляем из таблицы
                   Присвоение парам ассоциативной границ last_tracked_objects_aud NULL и флагу is_empty 1.
            End
   End
End actualize_exit_zones_data

Разрабатываем

Разработка шла по таким этапам: 

  • Перенос имеющихся методов из пет-проекта. 

  • Проектирование ER-модели приложения.

  • Реализация алгоритма переиндексации. 

  • Реализация и внедрение нейронной с расчётом идентичности двух обнаруженных объектов и принятием решения об индексации (в процессе).   

На этапе реализации механизма переиндексации столкнулся со сложностями. Во-первых, не был плотно знаком с ORM SQLAlchemy, из-за чего при разработке допускал ошибки. Во-вторых, изначально не придумал «триггеры», которые должны инициализировать переиндексацию. Сейчас они есть и занесены в отдельную таблицу, которая фиксирует входы и выходы из границ камер. Когда описывал принципы работы системы, рассказывал про нее.

Определяем дальнейшие шаги

Из планов: 

  • реализовать и внедрить нейросеть для расчета идентичности двух обнаруженных объектов и принятием решения об индексации (когда удастся это сделать, обязательно поделюсь результатами);  

  • реализация API приложения и разделения монолитного приложения на клиент и сервер.

Подчеркну мой личный итог: из пет-проекта, разработанного для защиты дипломной работы, вырос (почти) полноценный, применимый в реальном бизнесе инструмент. Надеюсь, что быстро справлюсь со всеми перечисленными сложностями и доработаю его. Будет здорово, если мой пример подтолкнет кого-то из читателей к развитию их пет-проектов или к созданию таковых.  

Буду рад вашим комментариям и вопросам. Если у кого-то есть идеи и соображения, как можно улучшить или оптимизировать работу системы и решить задачу по расчету идентичности — делитесь в комментариях. 

Комментарии (7)


  1. RoasterToaster
    31.10.2024 11:35

    а сделать приложение чтобы на территории ЖК, которого проблемы с парковкой, запирать чужие машины без скандалов? ( кто о чем, а мне парковаться негде))


    1. 0xC0CAC01A
      31.10.2024 11:35

      И как Вы себе это предствляете?


      1. RoasterToaster
        31.10.2024 11:35

        Ну как у нас в чате ) но в в виде нажал "припаркуй" и оно выдаст маршрут к машинам которые редко выезжают и отправит им уведомление "ЗАПЕР ВАС". Утром при выходе из дома жмешь кнопку "ВЫЕЗЖАЮ" и всем, кто запер приходит уведомление отогнать машину. Пока все так же и выглядит, но через телеграмм и звонок из машины на номер запершего.


        1. 0xC0CAC01A
          31.10.2024 11:35

          И чем это нагромождение лучше бумажки с номером телефона под боковым стеклом?


    1. danielsedoff
      31.10.2024 11:35

      Альтернативное решение. Чтобы не было скандалов, можно заменить жителей ЖК на новых, менее раздражительных.


      1. RoasterToaster
        31.10.2024 11:35

        Нене, все в целом молодцы. Запер - фотка номеров запертых в общий чат. Тут прямо напрашивается что-то автоматическое.


  1. Niakhm
    31.10.2024 11:35

    Добрый день. В чем преимущество по сравнению с системами с применением GPS. Система работает только при хорошем освещении (днем)? В промышленности остро стоит проблема взаимодействия техники. Часто самосвалы на горных карьерах наезжают на легковую технику. Может ваше решение работать для предотвращения подобных опасных ситуаций?