Учим нейросеть определять пол и возраст по картинке с камеры
medium

Учим нейросеть определять пол и возраст по картинке с камеры

Прикручиваем одну нейросеть к другой

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

Вот основные мысли про распознавания лиц, которые нам сегодня пригодятся:

  • Для распознавания лица компьютер должен получить изображение — через камеру или готовый файл.
  • Компьютер использует особый алгоритм, который разбивает изображение на прямоугольники. Так он пытается найти на картинке знакомые ему переходы между светлыми и тёмными областями.
  • Если в одном месте программа находит много таких совпадений, то, скорее всего, это лицо человека.
  • Чтобы программистам каждый раз не писать свой код распознавания с нуля, сделали специальную библиотеку компьютерного зрения — cv2. Если в неё загрузить заранее подготовленные параметры лиц, она сможет распознавать их намного точнее.
  • С помощью этой библиотеки мы можем получить координаты областей, которые нейросеть распознала как лица, чтобы нарисовать рамки вокруг них.
  • То, что внутри этих рамок, и будет лицом (по мнению нейронки).
  • Если повернуться вполоборота или сильно наклонить лицо вниз — может и не распознать.

Что делаем

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

Чтобы результат был виден сразу и наглядно, добавим вывод пола и возраста сразу над рамкой. Единственный момент: библиотека cv2 плохо работает с русской кодировкой и вместо русских букв выводит странные символы, поэтому надписи будут на английском:

Учим нейросеть определять пол и возраст по картинке с камеры
Возраст определяется с погрешностью

Вот код из прошлого проекта, его и будем дорабатывать.

# подключаем библиотеку компьютерного зрения 
import cv2

# функция определения лиц
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="opencv_face_detector.pbtxt"
# и конфигурацию самой нейросети — слои и связи нейронов
faceModel="opencv_face_detector_uint8.pb"

# запускаем нейросеть по распознаванию лиц
faceNet=cv2.dnn.readNet(faceModel,faceProto)

# получаем видео с камеры
video=cv2.VideoCapture(0)
# пока не нажата любая клавиша — выполняем цикл
while cv2.waitKey(1)<0:
    # получаем очередной кадр с камеры
    hasFrame,frame=video.read()
    # если кадра нет
    if not hasFrame:
        # останавливаемся и выходим из цикла
        cv2.waitKey()
        break
    # выводим картинку с камеры
    cv2.imshow("Face detection", frame)

Что понадобится

Чтобы не писать и не обучать новые нейросети с нуля, поступим как настоящие программисты — возьмём готовые и встроим в свой проект. Мы будем использовать модели, подготовленные Талом Хасснером и Гилом Леви.

С полом всё просто — мужской или женский, а вот прогнозируемый возраст может быть одним из следующих диапазонов:

  • 0–2 года, 
  • 4–6,
  • 8–12,
  • 15–20,
  • 25–32,
  • 38–43,
  • 48–53,
  • 60–100.

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

Качаем веса и параметры нейросети и кладём их в ту же папку, что и рабочий скрипт:

gender_deploy.prototxt

gender_net.caffemodel

age_deploy.prototxt

age_net.caffemodel

Загружаем новые модели в скрипт

Чтобы подключить новые нейросети к скрипту, используем тот же приём, что и с библиотекой cv2: загрузим файлы в DNN-модуль библиотеки cv2 и получим обученные нейронки, которые сразу готовы к работе.

Добавляем код после загрузки файлов cv2:

genderProto="gender_deploy.prototxt"
genderModel="gender_net.caffemodel"
ageProto="age_deploy.prototxt"
ageModel="age_net.caffemodel"

Теперь нам нужно подготовить специальные параметры для работы с изображениями, чтобы новые нейросети могли работать с найденными лицами. Дело в том, что нам нужно будет передать параметры лиц из одной нейронки в другие. Для этого выровняем освещение и сразу укажем итоговые параметры работы каждой новой нейросети:

# настраиваем свет
MODEL_MEAN_VALUES=(78.4263377603, 87.7689143744, 114.895847746)
# итоговые результаты работы нейросетей для пола и возраста
genderList=['Male ','Female']
ageList=['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']

Загружаем файлы в память:

# запускаем нейросети по определению пола и возраста
genderNet=cv2.dnn.readNet(genderModel,genderProto)
ageNet=cv2.dnn.readNet(ageModel,ageProto)

Добавляем вывод новых данных о поле и возрасте

В первый раз мы просто выводили картинку с рамкой, но сейчас этого недостаточно — нам нужно возле каждого лица подписать пол и возраст. Для этого сделаем так:

  1. Найдём все лица в кадре.
  2. Для каждого лица на основе рамок вокруг сформируем новый пиксельный объект.
  3. Отправим этот объект в нейросеть для определения пола.
  4. Получим результат работы этой нейросети.
  5. На основании результата выберем один из двух вариантов пола.
  6. То же самое сделаем для возраста.
  7. Полученные результаты добавим в виде текста поверх кадра возле рамки.

Всё это нужно добавить в итоговый цикл вместо простого вывода картинки:

# определяем лица в кадре
    resultImg,faceBoxes=highlightFace(faceNet,frame)
    # перебираем все найденные лица в кадре
    for faceBox in faceBoxes:
        # получаем изображение лица на основе рамки
        face=frame[max(0,faceBox[1]):
                   min(faceBox[3],frame.shape[0]-1),max(0,faceBox[0])
                   :min(faceBox[2], 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')

        # добавляем текст возле каждой рамки в кадре
        cv2.putText(resultImg, f'{gender}, {age}', (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)

Запускаем скрипт и смотрим, как нейросеть делает нас моложе:

Учим нейросеть определять пол и возраст по картинке с камеры

Но если у вас длинные волосы, то для нейросети это сразу весомый признак при определении пола:

Учим нейросеть определять пол и возраст по картинке с камеры

Что дальше

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

# подключаем библиотеку компьютерного зрения 
import cv2

# функция определения лиц
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="opencv_face_detector.pbtxt"
# и конфигурацию самой нейросети — слои и связи нейронов
faceModel="opencv_face_detector_uint8.pb"
# точно так же загружаем модели для определения пола и возраста
genderProto="gender_deploy.prototxt"
genderModel="gender_net.caffemodel"
ageProto="age_deploy.prototxt"
ageModel="age_net.caffemodel"

# настраиваем свет
MODEL_MEAN_VALUES=(78.4263377603, 87.7689143744, 114.895847746)
# итоговые результаты работы нейросетей для пола и возраста
genderList=['Male ','Female']
ageList=['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']

# запускаем нейросеть по распознаванию лиц
faceNet=cv2.dnn.readNet(faceModel,faceProto)
# и запускаем нейросети по определению пола и возраста
genderNet=cv2.dnn.readNet(genderModel,genderProto)
ageNet=cv2.dnn.readNet(ageModel,ageProto)

# получаем видео с камеры
video=cv2.VideoCapture(0)
# пока не нажата любая клавиша — выполняем цикл
while cv2.waitKey(1)<0:
    # получаем очередной кадр с камеры
    hasFrame,frame=video.read()
    # если кадра нет
    if not hasFrame:
        # останавливаемся и выходим из цикла
        cv2.waitKey()
        break
    # определяем лица в кадре
    resultImg,faceBoxes=highlightFace(faceNet,frame)
    # перебираем все найденные лица в кадре
    for faceBox in faceBoxes:
        # получаем изображение лица на основе рамки
        face=frame[max(0,faceBox[1]):
                   min(faceBox[3],frame.shape[0]-1),max(0,faceBox[0])
                   :min(faceBox[2], 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')

        # добавляем текст возле каждой рамки в кадре
        cv2.putText(resultImg, f'{gender}, {age}', (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)

Нейросети победили. Победите нейросети
Проходите подготовку на продвинутых курсах, чтобы не нейросети заменили вас, а вы создали нейросети. «Практикум» подобрал для вас фундаментальные и навыковые курсы, чтобы это сделать. Начните здесь, это бесплатно:
Начать бесплатно
Нейросети победили. Победите нейросети Нейросети победили. Победите нейросети Нейросети победили. Победите нейросети Нейросети победили. Победите нейросети
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
medium