В данной статье мы рассмотрим использование библиотеки OpenCV и моделей глубокого обучения (Deep Learning) в плате Raspberry Pi для точного определения пола и возраста изображения человека, получаемого из видео потока реального времени. Модели глубокого обучения для определения пола и возраста человека, которые мы будем использовать в нашем проекте, были разработаны учеными Levi и Hassner в 2015 году. Их статью, описывающую данные модели, вы можете прочитать по следующей ссылке (на английском языке).
Наш проект будет состоять из двух основных шагов (этапов):
- Обнаружение лиц людей в видео потоке реального времени.
- Извлечение области, существенной для анализа лица (Region of Interest, ROI) и использование на ней алгоритмов для определения пола и возраста человека.
Определяемый пол в нашем проекте будет ‘Male’ (мужской) или ‘Female’ (женский), а возраст мы будем определять в одном из следующих диапазонов: (0 – 2), (4 – 6), (8 – 12), (15 – 20), (25 – 32), (38 – 43), (48 – 53), (60 – 100).
Также на нашем сайте вы можете посмотреть другие проекты, в которых библиотека OpenCV использовалась в плате Raspberry Pi для обработки изображений:
- определение социальной дистанции с помощью Raspberry Pi и OpenCV;
- обнаружение движения на видео с помощью Raspberry Pi и OpenCV;
- сканер QR кодов на основе Raspberry Pi и OpenCV;
- обнаружение масок на лицах людей с помощью Raspberry Pi и OpenCV;
- распознавание лиц с помощью Raspberry Pi и библиотеки OpenCV.
Необходимые компоненты
- Плата Raspberry Pi (купить на AliExpress).
- Камера для Raspberry Pi (купить на AliExpress).
Установка OpenCV на Raspberry Pi
Перед установкой OpenCV и других необходимых пакетов обновите программное обеспечение Raspberry Pi до последней версии с помощью команды:
1 |
sudo apt-get update |
Затем используйте следующие команды, чтобы установить пакеты, необходимые для последующей установки библиотеки OpenCV.
1 2 3 4 5 6 |
sudo apt-get install libhdf5-dev -y sudo apt-get install libhdf5-serial-dev –y sudo apt-get install libatlas-base-dev –y sudo apt-get install libjasper-dev -y sudo apt-get install libqtgui4 –y sudo apt-get install libqt4-test –y |
После этого установите OpenCV на Raspberry Pi с помощью установщика pip:
1 |
pip3 install opencv-contrib-python==4.1.0.25 |
Объяснение программы для Raspberry Pi для распознавания пола и возраста человека
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в программе мы импортируем OpenCV и библиотеку для математических вычислений.
1 2 |
import cv2 import math |
Затем мы запрограммируем функцию highlightFace() для определений координат лица человека. В первых трех строках кода этой функции мы получим пустую копию кадра (frame), с помощью которой мы определим высоту и ширину кадра. Далее мы сконструируем blob (Binary Large Object – большой двоичный объект) и пропустим его через нейронную сеть для обнаружения лица. После этого в цикле по обнаруженным лицам мы извлечем координаты этих лиц, которые мы будем затем использовать для рисования ограничивающих прямоугольников вокруг лиц.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def highlightFace(net, frame, conf_threshold=0.7): frameOpencvDnn=frame.copy() frameHeight=frameOpencvDnn.shape[0] frameWidth=frameOpencvDnn.shape[1] blob=cv2.dnn.blobFromImage(frameOpencvDnn, 1.0, (300, 300), [104, 117, 123], True, False) net.setInput(blob) detections=net.forward() faceBoxes=[] for i in range(detections.shape[2]): confidence=detections[0,0,i,2] if confidence>conf_threshold: x1=int(detections[0,0,i,3]*frameWidth) y1=int(detections[0,0,i,4]*frameHeight) x2=int(detections[0,0,i,5]*frameWidth) y2=int(detections[0,0,i,6]*frameHeight) faceBoxes.append([x1,y1,x2,y2]) cv2.rectangle(frameOpencvDnn, (x1,y1), (x2,y2), (0,255,0), int(round(frameHeight/150)), 8) return frameOpencvDnn,faceBoxes |
В следующих строках кода мы укажем пути к моделям обнаружения лиц, определения пола и возраста.
1 2 3 4 5 6 |
faceProto="face_detector/opencv_face_detector.pbtxt" faceModel="face_detector/opencv_face_detector_uint8.pb" ageProto="age_detector/age_deploy.prototxt" ageModel="age_detector/age_net.caffemodel" genderProto="gender_detector/gender_deploy.prototxt" genderModel="gender_detector/gender_net.caffemodel" |
Далее укажем диапазоны определяемых возрастов и список определяемых полов (мужской, женский).
1 2 3 |
MODEL_MEAN_VALUES=(78.4263377603, 87.7689143744, 114.895847746) ageList=['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)'] genderList=['Male','Female'] |
Затем загрузим модели обнаружения лиц, определения пола и возраста с диска используя ранее указанные пути к ним.
1 2 3 |
faceNet=cv2.dnn.readNet(faceModel,faceProto) ageNet=cv2.dnn.readNet(ageModel,ageProto) genderNet=cv2.dnn.readNet(genderModel,genderProto) |
После этого инициализируем видео поток (video stream) и пробудим модуль камеры "ото сна".
1 |
video=cv2.VideoCapture(0) |
Далее, внутри цикла, мы будем извлекать кадры из видео потока и вызывать функцию highlightFace() с параметрами faceNet и frame. Возвращаемые функцией результаты будут сохраняться в переменных resultimg и faceboxes.
1 2 |
hasFrame,frame=video.read() resultImg,faceBoxes=highlightFace(faceNet,frame) |
После получения координат лиц мы будем из полученного изображения формировать 4-х размерный двоичный объект (blob). Мы будем его масштабировать, изменять его размер и передавать в него необходимые параметры.
1 |
blob=cv2.dnn.blobFromImage(face, 1.0, (227,227), MODEL_MEAN_VALUES, swapRB=False) |
Далее мы будем пропускать этот большой двоичный объект (blob) через модель определения пола и получать уровень доверия для двух классов - Male & Female. Для какого класса уровень доверия будет больше, тот и будет определенным на изображении полом человека.
1 2 3 |
genderNet.setInput(blob) genderPreds=genderNet.forward() gender=genderList[genderPreds[0].argmax()] |
Далее мы осуществим такую же процедуру с определением возраста человека.
1 2 3 |
ageNet.setInput(blob) agePreds=ageNet.forward() age=ageList[agePreds[0].argmax()] |
Теперь, когда у нас есть результаты определения пола и возраста, мы добавим их в результирующее изображение с помощью функции cv2.putText() и отобразим их с помощью функции imshow().
1 2 3 |
text = "{}:{}".format(gender, age) cv2.putText(resultImg, text,(faceBox[0], faceBox[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,255), 2, cv2.LINE_AA) cv2.imshow("Detecting age and gender", resultImg) |
Тестирование работы проекта
Перед тем, как запускать программу проекта на выполнение, подключите к плате Raspberry Pi модуль камеры как показано на следующем рисунке.
После этого проверьте корректно ли работает камера. Если все нормально, то запустите программу проекта на выполнение, в результате работы которой вы увидите в появившемся окне транслируемый с камеры видеопоток и на нем результаты распознавания пола и возраста человека (если на изображении с камеры присутствует лицо человека).
Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.
Исходный код программы на 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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import cv2 import math def highlightFace(net, frame, conf_threshold=0.7): frameOpencvDnn=frame.copy() frameHeight=frameOpencvDnn.shape[0] frameWidth=frameOpencvDnn.shape[1] blob=cv2.dnn.blobFromImage(frameOpencvDnn, 1.0, (300, 300), [104, 117, 123], True, False) net.setInput(blob) detections=net.forward() faceBoxes=[] for i in range(detections.shape[2]): confidence=detections[0,0,i,2] if confidence>conf_threshold: x1=int(detections[0,0,i,3]*frameWidth) y1=int(detections[0,0,i,4]*frameHeight) x2=int(detections[0,0,i,5]*frameWidth) y2=int(detections[0,0,i,6]*frameHeight) faceBoxes.append([x1,y1,x2,y2]) cv2.rectangle(frameOpencvDnn, (x1,y1), (x2,y2), (0,255,0), int(round(frameHeight/150)), 8) return frameOpencvDnn,faceBoxes faceProto="face_detector/opencv_face_detector.pbtxt" faceModel="face_detector/opencv_face_detector_uint8.pb" ageProto="age_detector/age_deploy.prototxt" ageModel="age_detector/age_net.caffemodel" genderProto="gender_detector/gender_deploy.prototxt" genderModel="gender_detector/gender_net.caffemodel" MODEL_MEAN_VALUES=(78.4263377603, 87.7689143744, 114.895847746) ageList=['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)'] genderList=['Male','Female'] faceNet=cv2.dnn.readNet(faceModel,faceProto) ageNet=cv2.dnn.readNet(ageModel,ageProto) genderNet=cv2.dnn.readNet(genderModel,genderProto) video=cv2.VideoCapture(0) padding=20 while True: hasFrame,frame=video.read() resultImg,faceBoxes=highlightFace(faceNet,frame) for faceBox in faceBoxes: face=frame[max(0,faceBox[1]-padding): min(faceBox[3]+padding,frame.shape[0]-1),max(0,faceBox[0]-padding) :min(faceBox[2]+padding, frame.shape[1]-1)] blob=cv2.dnn.blobFromImage(face, 1.0, (227,227), MODEL_MEAN_VALUES, swapRB=False) genderNet.setInput(blob) genderPreds=genderNet.forward() gender=genderList[genderPreds[0].argmax()] # print(f'Gender: {gender}') ageNet.setInput(blob) agePreds=ageNet.forward() age=ageList[agePreds[0].argmax()] # print(f'Age: {age[1:-1]} years') text = "{}:{}".format(gender, age) cv2.putText(resultImg, text,(faceBox[0], faceBox[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,255), 2, cv2.LINE_AA) cv2.imshow("Detecting age and gender", resultImg) key = cv2.waitKey(1) & 0xFF # если нажата клавиша `q`, то осуществляем выход из цикла if key == ord("q"): break # очищаем на экране результаты работы программы cv2.destroyAllWindows() vs.stop() |