Ich sage euch: man muß noch Chaos in sich haben, um einen tanzenden Stern gebären zu können (“Also sprach Zarathustra”, Friedrich Nietzsche)
Определение возраста с использованием OpenCV в Python
В последнее время возросло внимание к области компьютерного зрения, особенно к распознаванию лиц, обнаружению и локализации ориентиров на лицах. Многие важные черты, такие как возраст, пол и эмоции, можно напрямую понять по человеческому лицу.
Оценку возраста можно рассматривать как автоматический процесс классификации изображения лица по точному возрасту или определенному возрастному диапазону. По сути, оценка возраста по лицу все еще является сложной задачей, а угадать точный возраст по одному изображению очень сложно из-за таких факторов, как макияж, освещение, препятствия и выражения лица.
Вдохновленные множеством вездесущих приложений, распространяемых по разным каналам, таких как AgeBot на Android, «Age Calculator» на iPhone, мы собираемся создать простую оценку возраста, используя OpenCVએ в Python.
Основная цель этого урока — разработать на Python облегченную утилиту с командной строкой на основе модулей. Она должна автоматически обнаруживать лица на статическом изображении и прогнозировать возраст обнаруженных лиц с использованием модели определения возраста на основе глубокого машинного обучения.
В игру вступают следующие компоненты:
OpenCV: это библиотека с открытым исходным кодом для компьютерного зрения, машинного обучения и обработки изображений. OpenCV поддерживает широкий спектр языков программирования, таких как Python, C ++, Java, и используется для всех видов анализа изображений и видео, таких как обнаружение и распознавание лиц, редактирование фотографий и т.д. оптическое распознавание символов и еще много чего. Использование OpenCV дает множество преимуществ, среди которых:
OpenCV — это бесплатная библиотека с открытым исходным кодом.
OpenCV работает быстро, поскольку написан на C/C++.
OpenCV поддерживает большинство операционных систем, таких как Windows, Linux и macOS.
filetype: это небольшой пакет Python, не зависящий от зависимостей, для определения типов файлов и MIME.
В этом уроке будем использовать предварительно обученные модели Caffe, одну для распознавания лиц, взятую из урока по распознаванию лиц, и другую модель для определения возраста. Ниже приведен список необходимых файлов для включения в каталог нашего проекта:
age_net.caffemodel: это предварительно обученные веса модели для определения возраста. … Вы можете скачать егоздесь.
deploy_age.prototxt: это архитектура модели для модели определения возраста ( текстовый файл со структурой типа JSON, содержащий все определения уровня нейронной сети). Получитездесь.
res10_300x300_ssd_iter_140000_fp16.caffemodel: веса предварительно обученной модели для распознавания лиц, скачатьздесь.
deploy.prototxt.txt: это архитектура модели для обнаружения лиц. модель, загрузитездесь.
После загрузки 4 необходимых файлов, поместите их в папку weights:
Для начала установим OpenCV и NumPy:
$ pip install opencv-python numpy
Откройте новый файл Python:
# Import Libraries
import cv2
import os
import filetype
import numpy as np
# The model architecture
# download from: https://drive.google.com/open?id=1kiusFljZc9QfcIYdU2s7xrtWHTraHwmW
AGE_MODEL = 'weights/deploy_age.prototxt'
# The model pre-trained weights
# download from: https://drive.google.com/open?id=1kWv0AjxGSN0g31OeJa02eBGM0R_jcjIl
AGE_PROTO = 'weights/age_net.caffemodel'
# Each Caffe Model impose the shape of the input image also image preprocessing is required like mean
# substraction to eliminate the effect of illunination changes
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
# Represent the 8 age classes of this CNN probability layer
AGE_INTERVALS = ['(0, 2)', '(4, 6)', '(8, 12)', '(15, 20)',
'(25, 32)', '(38, 43)', '(48, 53)', '(60, 100)']
# download from: https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
FACE_PROTO = "weights/deploy.prototxt.txt"
# download from: https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel
FACE_MODEL = "weights/res10_300x300_ssd_iter_140000_fp16.caffemodel"
# Initialize frame size
frame_width = 1280
frame_height = 720
# load face Caffe model
face_net = cv2.dnn.readNetFromCaffe(FACE_PROTO, FACE_MODEL)
# Load age prediction model
age_net = cv2.dnn.readNetFromCaffe(AGE_MODEL, AGE_PROTO)
Здесь указаны пути для считывания фйлов весов и архитектуры наших моделей, размер изображения, к которому мы собираемся изменить размер, и, наконец, загрузили модели.
Переменная AGE_INTERVALS — это список возрастных классов модели определения возраста.
Затем давайте создадим функцию, которая принимает изображение в качестве входных данных и возвращает список обнаруженных лиц:
def get_faces(frame, confidence_threshold=0.5):
"""Returns the box coordinates of all detected faces"""
# convert the frame into a blob to be ready for NN input
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104, 177.0, 123.0))
# set the image as input to the NN
face_net.setInput(blob)
# perform inference and get predictions
output = np.squeeze(face_net.forward())
# initialize the result list
faces = []
# Loop over the faces detected
for i in range(output.shape[0]):
confidence = output[i, 2]
if confidence > confidence_threshold:
box = output[i, 3:7] * np.array([frame_width, frame_height, frame_width, frame_height])
# convert to integers
start_x, start_y, end_x, end_y = box.astype(int)
# widen the box a little
start_x, start_y, end_x, end_y = start_x - \
10, start_y - 10, end_x + 10, end_y + 10
start_x = 0 if start_x < 0 else start_x
start_y = 0 if start_y < 0 else start_y
end_x = 0 if end_x < 0 else end_x
end_y = 0 if end_y < 0 else end_y
# append to our list
faces.append((start_x, start_y, end_x, end_y))
return faces
Большая часть кода была взята из урока по распознаванию лиц, ознакомьтесь с ним, чтобы узнать, как это делается.
def display_img(title, img):
"""Displays an image on screen and maintains the output until the user presses a key"""
# Display Image on screen
cv2.imshow(title, img)
# Mantain output until user presses a key
cv2.waitKey(0)
# Destroy windows when user presses a key
cv2.destroyAllWindows()
Теперь мы знаем, как найти лица, функция ниже отвечает за прогнозирование возраста для каждого обнаруженного лица:
def predict_age(input_path: str):
"""Predict the age of the faces showing in the image"""
# Read Input Image
img = cv2.imread(input_path)
# resize the image
img = cv2.resize(img, (frame_width, frame_height))
# Take a copy of the initial image and resize it
frame = img.copy()
faces = get_faces(frame)
for i, (start_x, start_y, end_x, end_y) in enumerate(faces):
face_img = frame[start_y: end_y, start_x: end_x]
# image --> Input image to preprocess before passing it through our dnn for classification.
blob = cv2.dnn.blobFromImage(
image=face_img, scalefactor=1.0, size=(227, 227),
mean=MODEL_MEAN_VALUES, swapRB=False
)
# Predict Age
age_net.setInput(blob)
age_preds = age_net.forward()
print("="*30, f"Face {i+1} Prediction Probabilities", "="*30)
for i in range(age_preds[0].shape[0]):
print(f"{AGE_INTERVALS[i]}: {age_preds[0, i]*100:.2f}%")
i = age_preds[0].argmax()
age = AGE_INTERVALS[i]
age_confidence_score = age_preds[0][i]
# Draw the box
label = f"Age:{age} - {age_confidence_score*100:.2f}%"
print(label)
# get the position where to put the text
yPos = start_y - 15
while yPos < 15:
yPos += 15
# write the text into the frame
cv2.putText(frame, label, (start_x, yPos),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
# draw the rectangle around the face
cv2.rectangle(frame, (start_x, start_y), (end_x, end_y), color=(255, 0, 0), thickness=2)
# Display processed image
display_img('Age Estimator', frame)
# save the image if you want
# cv2.imwrite("predicted_age.jpg", frame)
Вот полный процесс выполнения вышеуказанной функции:
Мы считываем изображение с помощью метода cv2.imread().
После изменения размера изображения до соответствующего размера мы используем нашу функцию get_faces(), чтобы получить все обнаруженные лица.
Мы повторяем каждое изображение лица, устанавливаем его в качестве входных данных для модели прогнозирования возраста, чтобы выполнить прогнозирование возраста.
Мы печатаем вероятности каждого класса, а также доминирующего.
На изображении нарисован прямоугольник и текст, содержащий возраст.
Наконец, мы показываем окончательное изображение.
Вы всегда можете раскомментировать строку cv2.imwrite(), чтобы сохранить новое изображение.
Теперь давайте напишем наш основной код:
if __name__ == '__main__':
# Parsing command line arguments entered by user
import sys
image_path = sys.argv[1]
predict_age(image_path)
Мы просто используем встроенный в Python модуль sys для получения пользовательского ввода, так как нам нужен только один аргумент от пользователя и это путь к изображению, модуль argparse будет излишним.
Модель определения возраста сильно смещена в сторону возрастной группы [25–32], что легко понять при тестировании этой утилиты.
Всегда можно настроить некоторые параметры и сделать модель более точной. Например, в функции get_faces() я увеличил рамку на 10 пикселей со всех сторон. Всегда можно изменить это значение на любое другое, которое вам понравится. Изменение frame_width и frame_height также позволяет повысить точность прогноза.
Вот полный код для экспериментов из файла age-detection.py, а с архитектурой справитесь сами:
# Импорт библиотек
import cv2
import os
#import filetype
import numpy as np
# Архитектура модели
# получено по адресу: https://drive.google.com/open?id=1kiusFljZc9QfcIYdU2s7xrtWHTraHwmW
AGE_MODEL = 'weights/deploy_age.prototxt'
# Веса предварительно обученной модели
# получено по адресу: https://drive.google.com/open?id=1kWv0AjxGSN0g31OeJa02eBGM0R_jcjIl
AGE_PROTO = 'weights/age_net.caffemodel'
# Каждая модель Caffe определяет форму входного изображения, также требуется предварительная обработка изображения как среднее
# вычитание для устранения эффекта изменения освещенности
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
# Представьте 8 возрастных классов этого вероятностного слоя CNN
AGE_INTERVALS = ['(0, 2)', '(4, 6)', '(8, 12)', '(15, 20)',
'(25, 32)', '(38, 43)', '(48, 53)', '(60, 100)']
# получено по адресу: https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
FACE_PROTO = "weights/deploy.prototxt.txt"
# получено по адресу: https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel
FACE_MODEL = "weights/res10_300x300_ssd_iter_140000_fp16.caffemodel"
# Определение размера кадра
frame_width = 1280
frame_height = 720
# frame_width = 460
# frame_height = 566
# загрузить лицо модели Caffe
face_net = cv2.dnn.readNetFromCaffe(FACE_PROTO, FACE_MODEL)
# Загрузить модель прогнозирования возраста
age_net = cv2.dnn.readNetFromCaffe(AGE_MODEL, AGE_PROTO)
def get_faces(frame, confidence_threshold=0.5):
"""Возвращает прямоугольные координаты всех обнаруженных лиц"""
# преобразовать кадр в blob для ввода в NN
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), (104, 177.0, 123.0))
# учтановить катринку в качестве входа NN
face_net.setInput(blob)
# получить прогноз и показать его
output = np.squeeze(face_net.forward())
# инициализируем список для записи результата
faces = []
# Перебираем найденные лица
for i in range(output.shape[0]):
confidence = output[i, 2]
if confidence > confidence_threshold:
box = output[i, 3:7] * np.array([frame_width, frame_height, frame_width, frame_height])
# преобразовать в целые числа
start_x, start_y, end_x, end_y = box.astype(int)
# немного увеличим рамку
start_x, start_y, end_x, end_y = start_x - \
10, start_y - 10, end_x + 10, end_y + 10
start_x = 0 if start_x < 0 else start_x
start_y = 0 if start_y < 0 else start_y
end_x = 0 if end_x < 0 else end_x
end_y = 0 if end_y < 0 else end_y
# append to our list
faces.append((start_x, start_y, end_x, end_y))
return faces
def display_img(title, img):
"""Отображает изображение на экране пока пользователь не нажмет клавишу"""
# Показать картинку на экране
cv2.imshow(title, img)
# Показывем картинку пока не нажата какая-то клавиша
cv2.waitKey(0)
# Уничтожать окна при нажатии любой клавиши
cv2.destroyAllWindows()
def predict_age(input_path: str):
"""Предсказать возраст лиц на фотографии"""
# Читать изображение
img = cv2.imread(input_path)
# Изменим размер
img = cv2.resize(img, (frame_width, frame_height))
# Take a copy of the initial image and resize it
frame = img.copy()
faces = get_faces(frame)
for i, (start_x, start_y, end_x, end_y) in enumerate(faces):
face_img = frame[start_y: end_y, start_x: end_x]
# image --> Input image to preprocess before passing it through our dnn for classification.
blob = cv2.dnn.blobFromImage(
image=face_img, scalefactor=1.0, size=(227, 227),
mean=MODEL_MEAN_VALUES, swapRB=False
)
# Прогнозировать возраст
age_net.setInput(blob)
age_preds = age_net.forward()
print("="*30, f"Face {i+1} Prediction Probabilities", "="*30)
for i in range(age_preds[0].shape[0]):
print(f"{AGE_INTERVALS[i]}: {age_preds[0, i]*100:.2f}%")
i = age_preds[0].argmax()
age = AGE_INTERVALS[i]
age_confidence_score = age_preds[0][i]
# Нарисуйте прямоугольник
label = f"Age:{age} - {age_confidence_score*100:.2f}%"
print(label)
# получаем позицию, куда поместить текст
yPos = start_y - 15
while yPos < 15:
yPos += 15
# вписываем текст в рамку
cv2.putText(frame, label, (start_x, yPos),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
# рисуем прямоугольник вокруг лица
cv2.rectangle(frame, (start_x, start_y), (end_x, end_y), color=(255, 0, 0), thickness=2)
# Показать результат на экране
display_img('Age Estimator', frame)
# Сохранить изображение
cv2.imwrite("predicted_age.jpg", frame)
if __name__ == '__main__':
# Разбор аргументов командной строки
import sys
image_path = sys.argv[1]
predict_age(image_path)