Подход на основе ИИ к решению «проблемы» физического дистанцирования
По мере того, как страны начинают вынашивать идею ослабления норм изоляции, поддержание физического расстояния более 5 футов или выше между людьми, которые наконец выходят на улицу, может сыграть решающую роль в сдерживании распространения Covid-19.
Меня впервые познакомила с этой идеей (Coldewey, 2020) компания по разработке программного обеспечения в Сан-Франциско, Калифорния, Zensors. Эта программа отслеживает и отслеживает толпу в режиме реального времени с помощью видеозаписи с камер видеонаблюдения, высматривая людей или группы, приближающиеся друг к другу менее чем на 5–6 футов. Такие группы обведены красной рамкой, чтобы информировать об этом контролирующие органы.
Человек, постоянно наблюдающий за камерами безопасности, чтобы определить красные блоки для нарушения, может звучать как пустая трата ценных человеческих ресурсов. Таким образом, я интегрировал в программу функцию рассылки, которая мгновенно информирует власти о любых нарушениях дистанцирования, как только обнаруживает их. Определение нарушения может быть установлено в соответствии с критериями, установленными местными властями, которые может варьироваться от 2 человек, идущих вместе, до собрания более 20 человек. Ниже приведен код и объяснение. Он был скомпилирован в Google Collab со средой выполнения GPU.
import time import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.base import MIMEBase from email import encoders from google.colab.patches import cv2_imshow from google.colab import drive import pandas as pd import cv2 import math import numpy as np drive.mount(“/GD”)
Я решил использовать очень простой классификатор, а именно каскадные классификаторы Хаара для обнаружения всего тела, который является предварительно обученным классификатором, разработанным (Paul Viola, 2001). Если вы хотите узнать больше о модуле обнаружения на этом классификаторе, вы можете прочитать это здесь. Это доступно в библиотеке OpenCV. Недавние разработки привели к созданию лучших и более быстрых классификаторов, таких как более быстрые R-CNNS и YOLO, которые я попытаюсь использовать в будущих версиях для улучшения этой модели.
person_clf = cv2.CascadeClassifier(‘/GD/MyDrive/dataset/haarcascade_fullbody.xml') # Initiate video capture for video file cap = cv2.VideoCapture(‘/GD/My Drive/dataset/mall_walking.avi’) writer = None OUTPUT=’/GD/My Drive/dataset/output_’+str(YOUROUTPUTFILENAMEHERE)
Здесь экземпляр классификатора был загружен в person_clf, и был установлен выходной каталог для анализируемого видео с отметками нарушений. cv2.VideoCapture используется для загрузки нашего тестового видео и является встроенной вызываемой функцией объекта cv2.
def center(c): x_start=d[‘box’+str(c)+’x’] y_start=d[‘box’+str(c)+’y’] x_end=d[‘box’+str(c)+’x+w’] y_end=d[‘box’+str(c)+’y+h’] Point=int((x_start+x_end)/2), int((y_start+y_end)/2) return Point def mailer(pic,mail): path=”/GD/My Drive/dataset/”+pic from = “[YOURMAILHERE]” to = "[TARGETMAILHERE]" message = MIMEMultipart() message[‘From’] = from message[‘To’] = to message[‘Subject’] = “Violation notification” body = “Dear user, pfa for instances of violation of social distancing” msg.attach(MIMEText(body, ‘plain’)) filename = pic attachment = open(path, “rb”) p = MIMEBase(‘application’, ‘octet-stream’) p.set_payload((attachment).read()) # encode into base64 encoders.encode_base64(p) p.add_header(‘Content-Disposition’, “attachment; filename= %s” % filename) msg.attach(p) s = smtplib.SMTP(‘smtp.gmail.com’, 587) s.starttls() s.login(from, “YOURPASSWORDHERE”) text = msg.as_string() s.sendmail(from, to, text) s.quit()
Создание функций для вашего проекта — это хороший способ помочь в переносе обучения при выполнении аналогичной работы. Таким образом, я сделал здесь две базовые функции: одну для поиска центров между двумя точками, а другую для отправки почтовых программ цели с вложениями. Ниже я подробно объясню функцию центра. Функция рассыльщика берет имя изображения, которое хранится на моем диске Google, с моментом нарушения, прикрепляет его к письму с телом сообщения и отправляет его адресату. Я нашел эту статью здесь действительно информативной и использовал ее, чтобы включить это.
Прямо сейчас к основной функции. Чтобы понять основной код, мы должны сначала понять иерархию, в которой работают различные виды циклов, и работу, которую они выполняют.
Объяснение кода
i=1 contn_frame_with_detection=0 skipped_frames=0 while cap.isOpened():
Мы вызываем экземпляр шапки, в котором хранится наше входное видео, и открываем его, чтобы мы могли обрабатывать один кадр за раз. Это наш первый цикл while, поэтому весь код будет внутри него. Также начинаем 3 счетчика. «I» для подсчета обрабатываемых кадров, «contn_frame_with_detection» для сохранения количества непрерывных кадров, в которых было обнаружено нарушение, и «skiped_frames» для подсчета кадров между обнаруженными кадрами, в которых не было обнаружено нарушений. Идея пропущенных кадров будет обсуждаться позже.
frame_violator=0 l=[] d={} person_id=0 ret,frame = cap.read() if not ret: break
Здесь инициируется еще один счетчик «frame_violator» для подсчета количества обнаруженных нарушений в каждом кадре. Некоторые власти могут захотеть оставить его высоким, если им нужно обнаруживать только большие собрания и игнорировать 2,3 человека, идущих вместе. person_id устанавливается равным 1 в ожидании обнаружения первого человека в первом кадре. ‘cap.read()’ помогает нам прочитать кадр и сохранить его для дальнейшей обработки. Он также получает указание прерываться, когда кадр заканчивается.
frame = cv2.resize(frame, None,fx=0.95, fy=0.95, interpolation = cv2.INTER_LANCZOS4) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) persons = person_clf.detectMultiScale(gray, 1.02, 5)
Сначала нам нужно изменить размер кадра с помощью cv2.resize() примерно до 60%, чтобы вычисления можно было выполнять в реальном времени без ущерба для скорости. Если у вас более быстрые процессоры, чем у Google Collab Gpu, вы можете оставить его выше. cv2.INTER_LANCZOS4 обычно считается лучшим методом интерполяции, но другие методы, такие как INTER_LINEAR, INTER_AREA, также могут использоваться в зависимости от повышения или понижения дискретизации изображения. Метод COLOR_BGR2GRAY используется для преобразования изображения BGR (именно так OpenCV читает изображения), имеющего 3 канала, в оттенки серого, имеющего только 1 канал. Как можно видеть, при выравнивании слоев свертки вычисления выполняются намного ниже в оттенках серого без потери большого количества информации, и, следовательно, их легче обрабатывать машине. Следовательно, мы используем метод detectMutiScale cv2 для обнаружения людей в данном кадре из серого изображения. Следует отметить, что это возвращает начальную и конечную координаты диагонали прямоугольника, внутри которого можно найти обнаруженного человека. Мы сохраняем эти координаты внутри переменной person, чтобы позже перебирать их одну за другой.
for (x,y,w,h) in persons: d[‘box’+str(person_id)+’x’]=x d[‘box’+str(person_id)+’y’]=y d[‘box’+str(person_id)+’x+w’]=x+w d[‘box’+str(person_id)+’y+h’]=y+h l.append(center(int(person_id))) if person_id>0: s=0 feed_detected=0 for mid in l[:-1]: dist = math.sqrt((mid[0] — center(int(person_id)) [0])**2 + (mid[1] — center(int(person_id))[1])**2) if dist<=40: cv2.rectangle(frame, (d[‘box’+str(s)+’x’], d[‘box’+str(s)+’y’]), (d[‘box’+str(s)+’x+w’], d[‘box’+str(s)+’y+h’]), (0, 0,255), 2) cv2.line(frame,(int(mid[0]),int(mid[1])),(center(int(person_id))[0],center(int(person_id))[1]),(0,0,255),(2)) feed_detected+=1 frame_violator+=1 s+=1 if feed_detected>0: cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 2) person_id+=1
Приведенный выше код формирует логическое ядро этого алгоритма. Сначала мы начинаем перебирать каждого человека индивидуально(для цикла A)и сохраняем координаты прямоугольника в словаре python, названном в формате ['box-person_id-x']: значение. Затем мы вызываем нашу функцию center, чтобы найти центр масс этого человека и сохранить его в списке. Функция center принимает идентификатор person_id, центр масс которого необходимо вычислить, извлекает его данные из словаря начальной и конечной точек и вычисляет координату центра. Теперь в соответствии с нашей иерархией инициализируется второй цикл(для цикла B), когда person_id в кадре больше 0 (фактически означает более двух человек), его расстояние от каждого из предыдущих центр масс обнаруженного человека вычисляется по простой формуле евклидова расстояния. Если обнаружено нарушение, значение параметра feed_detector увеличивается на единицу, указывая на то, что входящий фид имеет нарушение. Обнаруженные нарушения обводятся красной рамкой, а cv2.rectangle сохраняет размер цвета (0,0,255), что указывает на красный цвет. для формата BGR, который используется cv2.
if frame_violator>0 and skipped_frames<4: contn_frame_with_detection+=1 skipped_frames=0 else: skipped_frames+=1 if skipped_frames>=4: contn_frame_with_detection=0 skipped_frames=0 i+=1
Счетчик пропущенных кадров введен для учета ложных отрицательных результатов нашей модели. Между двумя обнаруженными кадрами нарушения будет много кадров, где классификатор HAAR может быть не в состоянии даже обнаружить всех людей. Это приведет к ложной идентификации кадра как отсутствия нарушения. Таким образом, когда мы видим количество непрерывных кадров, в которых было обнаружено нарушение, мы также должны видеть, удерживаются ли пропущенные кадры ниже определенного числа. Если skipped_frame больше, чем, скажем, 5, это может означать, что кадры, в которых ранее происходило нарушение, на самом деле могли быть просто ложными срабатываниями, и мы должны снова, таким образом, установить для Continuous_frame_counter значение 0, а для skipped_frames — 0, чтобы снова начать подсчет.
if contn_frame_with_detection>25: contn_frame_with_detection=0 skipped_frames=0 name=”pic”+str(i)+”.jpg” cv2.imwrite(‘/GD/My Drive/dataset/’+name,frame) mailer(name)
Чтобы закрепить обнаружение как истинное срабатывание, мы рассматриваем серьезное нарушение расстояния только тогда, когда Continuous_frame_counter превышает 25 (или выше, в зависимости от потребностей пользователя).
мы делаем снимок в этот момент, вызывая функцию cv2.imwrite и вызывая нашу предопределенную функцию рассылки, чтобы передать имя изображения jpg. Таким образом, эта функция почтовой программы мгновенно отправляет письмо целевому пользователю, информируя его о ситуации.
if writer is None: vid_write= cv2.VideoWriter_fourcc(*”XVID”) writer = cv2.VideoWriter(OUTPUT, vid_write, 35,(frame.shape[1], frame.shape[0]), True) writer.write(frame)
Наконец, мы создаем экземпляр cv2.VideoWriter и сохраняем наш видеофайл в формате XviD, по одному кадру за раз.
#this will outside main while loop writer.release() cap.release() cv2.destroyAllWindows()
После достижения конца кадров мы отпускаем средство записи, закрываем и уничтожаем все окна с помощью cv2.destroyAllWindows(), чтобы компьютер не зависал. Ниже приведен пример вывода программы.
Итак, это довольно много циклов, чтобы понять их за один день. Я пытался создать каждую функцию с нуля и реализовать все это с помощью логики, таким образом, эти сложные циклы. Я буду публиковать информацию о том, как развернуть это в Интернете, используя, возможно, экземпляр EC2 Azure ml. Надеюсь, вам понравилась интуиция, стоящая за программой, и вы смогли оценить различные счетчики и циклы, использованные для ее создания. Любой, у кого есть какие-либо идеи, чтобы, возможно, добавить какую-то функцию в эту программу или ускорить ее работу, определенно может обратиться ко мне. До тех пор, живите долго и с наукой о данных.
ИСТОЧНИКИ
Колдеви, Д. (2020, апрель). https://techcrunch.com/activity-monitoring-startup-zensors-repurposes-its-tech-to-help-coronavirus-response. Получено с сайта Techcrunch: https://techcrunch. com/2020/04/02/активность-мониторинг-запуск-zensors-перепрофилирование-его-технология-помощь-коронавирус-ответ/
Джонс, PV (2001). opencv. Получено с Github: https://github.com/opencv/opencv/tree/master/data/haarcascades
Пол Виола, MJ (2001). Быстрое обнаружение объектов с помощью расширенного каскада простых функций. www.cs.cmu.edu.