Распознавание лиц в настоящее время применяется во множестве разнообразных устройств и технологий. Но прежде чем производить операцию распознавания лица человека, необходимо это лицо сначала обнаружить в видеопотоке или на изображении и, если требуется, производить отслеживание этого лица во время его перемещений.
В данной статье мы рассмотрим отслеживание человеческих лиц с помощью платы Arduino, библиотеки OpenCV и языка программирования Python. Для обнаружения лица человека в видеопотоке мы будем использовать каскады (классификаторы) Хаара (Harr cascade classifiers) из библиотеки OpenCV, а для отслеживания его местоположения мы применим сервомотор, который будет управлять движениями камеры. Управляться сервомотор будет с помощью команд от платы Arduino.
Ранее на нашем сайте мы уже рассматривали проект робота на Arduino, распознающего и отслеживающего лица, в основе работы которого лежало специальное приложение для Android. Но данный проект отслеживания лиц с помощью Arduino является по сравнению с ним более “продвинутым”, поскольку использует возможности такой общепризнанной и весьма мощной библиотеки для обработки изображений как OpenCV.
Необходимые компоненты
Аппаратное обеспечение
- Плата Arduino Uno (купить на AliExpress).
- USB камера.
- Сервомотор SG90 (2 шт.) (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Программное обеспечение
- Arduino IDE.
- Python 3.8.
Общие принципы работы проекта
Алгоритмы обнаружения человеческих лиц идентифицируют и локализуют местоположение лиц, игнорируя при этом все фоновые объекты: занавески, ока, деревья и др. Библиотека OpenCV для обнаружения лиц использует так называемые классификаторы Хаара. В процессе анализа каждый кадр видео потока последовательно проходит через несколько классификаторов и если он прошел через все классификаторы, то лицо считается обнаруженным, иначе кадр отбрасывается, то есть обнаружение лица на нем не фиксируется.
Библиотека OpenCV возвращает декартовы координаты (x и y) фрагмента изображения с обнаруженным лицом с высотой (height) и шириной (width) изображения. После этого вычислить координаты центра этого фрагмента изображения можно в соответствии с выражениями: x+width/2 и y+height/2.
Ранее на нашем сайте мы рассматривали обработку изображений с помощью библиотеки OpenCV, запущенной на плате Raspberry Pi, в следующих проектах:
- обнаружение движения на видео с помощью Raspberry Pi и OpenCV;
- определение пола и возраста людей с помощью Raspberry Pi и OpenCV;
- обнаружение масок на лицах людей с помощью Raspberry Pi и OpenCV;
- распознавание эмоций с помощью Raspberry Pi и OpenCV;
- распознавание лиц с помощью Raspberry Pi и библиотеки OpenCV.
Разумеется, вычислительные возможности платы Arduino Uno значительно скромнее, чем у платы Raspberry Pi, поэтому библиотеку OpenCV на ней непосредственно использовать нельзя. Да и объем памяти не получит. Поэтому в нашем случае использование библиотеки OpenCV для обнаружения лиц производится с помощью программы на Python, запущенной на персональном компьютере или ноутбуке. В этом случае принцип работы нашего проекта можно изобразить следующим рисунком:
Далее координаты фрагмента изображения с обнаруженным лицом человека по последовательному каналу связи передаются с компьютера в плату Arduino Uno с помощью библиотеки pyserial. На основе значений этих координат плата Arduino Uno управляет двумя подключенными к ней сервомоторами (как подключить сервомотор к плате Arduino можно прочитать в этой статье), которые и реализуют механизм поворота/наклона USB камеры, с которой осуществляется считывание видео потока. Если координаты центра обнаруженного лица находятся далеко от центра изображения, то производится коррекция положения камеры на 2 градуса в сторону, обеспечивающую приближение центра лица к центру изображения.
Более подробно про способы взаимодействия программы на Python, запущенной на компьютере, с платой Arduino, вы можете прочитать в данной статье.
Обнаружение лица
Автор проекта (ссылка на оригинал приведена в конце статьи) для обнаружения лиц использовал файл 'haarcascade_frontalface_default.xml'. Этот файл представляет собой заранее обученную модель для распознавания человеческих лиц и его можно скачать по этой ссылке.
Далее в программу его можно подключить с помощью команды:
1 |
cv2.CascadeClassifier('haarcascade_frontalface_default.xml') |
После этого для обнаружения лиц в программе можно использовать функцию cv2.CascadeClassifier.detectMultiScale(), в которой значение 'scale factor' установлено равным 1.1 (значение по умолчанию), а значение 'minNeighbour' равно 6. Эта функция возвращает декартовы координаты изображения вместе с его высотой и шириной (height и width). Увеличение значения 'minNeighbour' может улучшить качество обнаружения (точность) лиц, однако приведет к уменьшению скорости работы программы, что, в свою очередь, приведет к задержке команды, подаваемой на сервомоторы. Автор проекта в результате проведенных экспериментов определил, что значение 6 параметра 'minNeighbour' в данном случае является оптимальным.
Для улучшения точности определения положения лиц желательно чтобы фон на заднем плане изображения был по возможности однородным.
Расчет координат
Библиотека OpenCV возвращает координаты обнаруженного лица в пикселах. По умолчанию разрешение видео устанавливается равным 640*480. Возвращаемые координаты определяют верхний левый угол изображения вместе со значениями его высоты и ширины (height и width). Мы будем рассчитывать координаты центра изображения по формулам x+width/2 и y+height/2 и показывать его на изображении зеленой точкой. Далее эти координаты передаются плате Arduino, которая дает команды сервомоторам на перемещение камеры вверх/вниз и влево/вправо.
Когда центр лица определен, то зеленая точка должна находиться внутри белого прямоугольника. Если она находится вне этого прямоугольника, то значит лицо переместилось и дается команда на соответствующее перемещение камеры.
Передача последовательных данных плате Arduino
Автор проекта провел много экспериментов прежде чем нашел быстрый способ последовательной передачи найденных значений координат в плату Arduino. В результате он решил использовать функцию Serial.parseInt(), которая извлекает числа целого типа (integer) из поступающего последовательного потока байтов. Описание этой функции можно посмотреть здесь.
Программа на python в нашем проекте передает координаты центра изображения в виде строки, например, в строке "X100Y200" значение 100 будет соответствовать координате x, а значение 200 – координате y.
Управление сервомоторами
Автор проекта закрепил сервомоторы на камере с помощью стержней и обычных резинок. Внешний вид получившейся у него конструкции проекта показан на следующем рисунке.
Поскольку в проекте используется два сервомотора, то рекомендуется дополнительно запитать плату Arduino от адаптера 9V, потому что питания, получаемого ею через USB кабель, будет не хватать для удовлетворительного управления двумя сервомоторами и в этом случае могут наблюдаться нежелательные вибрации при их движениях.
Используемые библиотеки
Для проекта требуются библиотеки pyserial и opencv, которые можно скачать с помощью установщика pip. Автор проекта использовал версию python 3.8 и версию OpenCV 4.4.0, поэтому перед реализацией проекта убедитесь в том, что у вас такие же версии python и OpenCV, или выше. Также удостоверьтесь в том, что скачанный XML файл с моделью обнаружения лиц помещен в тот же самый каталог, откуда вы будете запускать скрипт на python.
Также вам необходимо внести изменения в строку 9 представленного далее кода программы – в ней вы должны изменить номер COM порта компьютера, к которому подключена плата Arduino, на свой.
Пример работы проекта вы можете посмотреть на следующем gif изображении.
Исходные коды программ
Код скетча для Arduino
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
//Face Tracker using OpenCV and Arduino //by Shubham Santosh #include<Servo.h> Servo x, y; int width = 640, height = 480; // total resolution of the video (разрешение видео) int xpos = 90, ypos = 90; // initial positions of both Servos (начальная позиция обоих сервомоторов) void setup() { Serial.begin(9600); x.attach(9); y.attach(10); // Serial.print(width); //Serial.print("\t"); //Serial.println(height); x.write(xpos); y.write(ypos); } const int angle = 2; // degree of increment or decrement (число градусов, на которое осуществляется поворот камеры за одно действие) void loop() { if (Serial.available() > 0) { int x_mid, y_mid; if (Serial.read() == 'X') { x_mid = Serial.parseInt(); // read center x-coordinate (считываем x-координату) if (Serial.read() == 'Y') y_mid = Serial.parseInt(); // read center y-coordinate (считываем y-координату) } /* adjust the servo within the squared region if the coordinates is outside it (вычисляем параметры корректировки положения камеры если зеленая точка находится за пределами белого прямоугольника) */ if (x_mid > width / 2 + 30) xpos += angle; if (x_mid < width / 2 - 30) xpos -= angle; if (y_mid < height / 2 + 30) ypos -= angle; if (y_mid > height / 2 - 30) ypos += angle; // if the servo degree is outside its range (если рассчитанное значение градуса находится вне допустимого диапазона) if (xpos >= 180) xpos = 180; else if (xpos <= 0) xpos = 0; if (ypos >= 180) ypos = 180; else if (ypos <= 0) ypos = 0; x.write(xpos); y.write(ypos); // used for testing //Serial.print("\t"); // Serial.print(x_mid); // Serial.print("\t"); // Serial.println(y_mid); } } |
Код программы на python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#Face tracker using OpenCV and Arduino #by Shubham Santosh import cv2 import serial,time face_cascade= cv2.CascadeClassifier('haarcascade_frontalface_default.xml') cap=cv2.VideoCapture(1) #fourcc= cv2.VideoWriter_fourcc(*'XVID') ArduinoSerial=serial.Serial('com7',9600,timeout=0.1) #out= cv2.VideoWriter('face detection4.avi',fourcc,20.0,(640,480)) time.sleep(1) while cap.isOpened(): ret, frame= cap.read() frame=cv2.flip(frame,1) #mirror the image (зеркало изображения) #print(frame.shape) gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY) # конвертируем изображение в серое faces= face_cascade.detectMultiScale(gray,1.1,6) #detect the face (обнаруживаем лицо) for x,y,w,h in faces: #sending coordinates to Arduino (передаем координаты в Arduino) string='X{0:d}Y{1:d}'.format((x+w//2),(y+h//2)) print(string) ArduinoSerial.write(string.encode('utf-8')) #plot the center of the face cv2.circle(frame,(x+w//2,y+h//2),2,(0,255,0),2) #plot the roi (рисуем область, существенную для анализа) cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),3) #plot the squared region in the center of the screen (рисуем белый прямоугольник в центре изображения) cv2.rectangle(frame,(640//2-30,480//2-30), (640//2+30,480//2+30), (255,255,255),3) #out.write(frame) cv2.imshow('img',frame) #cv2.imwrite('output_img.jpg',frame) '''for testing purpose read= str(ArduinoSerial.readline(ArduinoSerial.inWaiting())) time.sleep(0.05) print('data from arduino:'+read) ''' # press q to Quit (нажмите q для выхода из программы) if cv2.waitKey(10)&0xFF== ord('q'): break cap.release() cv2.destroyAllWindows() |
здраствуйте, я хотел делать но трудности есть можете подробнее расказать
Добрый вечер. К сожалению не могу. Эту статью я только перевел, но ни я, ни мои студенты пока данный проект не собирали. Если у вас есть какой то конкретный вопрос по данному проекту, то могу попробовать на него ответить
Здравствуйте.
Можно ли вместо двух сервомоторов sg90, использовать один sg90, а второй шаговый двигатель 28byj-48 ?
Добрый вечер. Теоретически проблем не вижу, с помощью шагового двигателя можно будет даже точнее позиционировать положение камеры. Необходимо будет только программу немного изменить. Вот, кстати, можете статью прочитать о подключении шагового двигателя 28byj-48 к плате Arduino.