年龄性别预测4:C/C++实现年龄性别预测和识别(含源码,可实时预测)
目录
年龄性别预测4:C/C++实现年龄性别预测和识别(含源码,可实时预测)
(2)配置开发环境(OpenCV+OpenCL+base-utils+TNN)
本项目将实现年龄性别预测和识别,整套方案采用二阶段方法实现,即首先使用通用的人脸检测算法(Face Detection)定位人脸区域,裁剪人脸,再构建多任务模型,分别进行年龄预测(Age)和性别识别(Gender)。项目分为数据集说明,Pytorch模型训练和C++/Android部署等多个章节,本篇是项目《年龄性别预测》系列文章之C/C++实现年龄性别预测和识别;本篇主要分享将Python训练后的年龄性别预测和识别模型移植到C/C++平台。我们将开发一个简易的、可实时检测的年龄性别识别C/C++ Demo;项目源码模型推理支持CPU和GPU加速,开启GPU(OpenCL)加速,可以达到实时的检测识别效果,基本满足业务的性能需求。
C++版本与Python版本的结果几乎是一致,下面是年龄性别预测和识别效果展示
【尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/135556843
更多项目《年龄性别预测》和《面部表情识别》系列文章请参考:
- 面部表情识别1:表情识别数据集(含下载链接)
- 面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)
- 面部表情识别3:Android实现表情识别(含源码,可实时检测)
- 面部表情识别4:C++实现表情识别(含源码,可实时检测)
- 年龄性别预测1:年龄性别数据集说明(含下载地址)
- 年龄性别预测2:Pytorch实现年龄性别预测和识别(含训练代码和数据)
- 年龄性别预测3:Android实现年龄性别预测和识别(含源码,可实时预测)
- 年龄性别预测4:C/C++实现年龄性别预测和识别(含源码,可实时预测)
1.年龄性别预测和识别方法
年龄性别预测和识别方法有多种实现方案,这里采用最常规的二阶段方法实现,即首先使用通用的人脸检测算法(Face Detection)定位人脸区域,裁剪人脸,再构建多任务模型,分别进行年龄预测(Age)和性别识别(Gender)。
- 人脸检测:人脸检测算法已经有很多成熟开源项目了,本项目不作分析,可以参考使用MTCNN,DSFD,FaceBoxes,RFB等方法。
- 性别识别:性别识别是一个简单二分类,训练使用交叉熵损失函数即可
- 年龄预测:可以采用年龄分类方法,如SSR-Net模型,也可以结合回归的方法,如Label Distribution。就调研而言,基于Label Distribution的方法会比分类方法准确率会高一些。
下图本项目构建的年龄性别预测和识别模型,其中
- Backbone:主杆网络,用于提取人脸图像特征,可以使用任意的骨干网络,如resnet18,resnet34,resnet50以及轻量化模型mobilenet_v2等
- Gender-branch: 性别识别分支,用于对性别进行分类识别,损失函数使用交叉熵
- Age-branch: 年龄预测分支,对年龄进行预测,损失函数可以使用交叉熵或者Label Distribution,项目设定最大周岁是70周岁,训练数据中年龄大于70的lalel,会重置为70。
2.人脸检测方法
本项目人脸检测训练代码请参考:https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB
这是一个基于SSD改进且轻量化后人脸检测模型,很轻巧,整个模型仅仅1.7M左右,在普通Android手机都可以实时检测。人脸检测方法在网上有一大堆现成的方法可以使用,如MTCNN,DSFD,FaceBoxes,完全可以不局限我这个方法。
关于人脸检测的方法,可以参考我的另一篇博客:
行人检测和人脸检测和人脸关键点检测(C++/Android源码)
3.年龄性别预测和识别模型(Python)
(1) 年龄性别预测和识别模型训练
本篇博文不含Python版本的模型以及相关训练代码,关于年龄性别预测和识别模型的训练方法,请参考本人另一篇博文《Pytorch实现年龄性别预测和识别(含训练代码和数据)》;
Python项目源码backbone模型支持的有resnet18,resnet50,以及轻量化模型mobilenet_v2等常见的深度学习模型,用户也可自定义模型进行训练;准确率还挺高的,采用轻量级mobilenet_v2模型的性别识别准确率0.9603左右,年龄预测MAE(平均绝对误差:)3.1935左右,CS5(预测年龄与真实年龄的绝对误差不过5年的准确率)0.8021左右,基本满足业务性能需求。
模型 | input size | 性别准确率 | 年龄MAE | 年龄CS3 | 年龄CS5 |
AE_mobilenet_v2 | 112×112 | 0.9603 | 3.1935 | 0.5969 | 0.8021 |
AE_resnet18 | 112×112 | 0.9606 | 3.1795 | 0.5956 | 0.8010 |
AE_resnet50 | 112×112 | 0.9609 | 3.2008 | 0.5900 | 0.8035 |
关于模型训练以及测试的建议:
- 关于性别识别的问题:目前性别识别的准确率约96%,识别错误的主要有两种情况,(1)儿童性别容易误识别,特别是1~3岁左右的儿童,性别识别比较困难 (2) 长头发的男生或者短头发的女生的,也容易误识别;其他情况,正常穿着打扮的男士女生识别准确率可以达到99%左右。
- 关于年龄预测的问题:现有数据年龄部分不均匀,大部分人脸数据年龄分布在20-40岁之间的年轻人,而儿童和老年人的数据比较少;导致儿童和老年人年龄预测精准度比较差;另外,也是强烈建议的:采集同一个人不同年龄阶段的人脸数据加入模型训练,可以有效提升年龄预测的精准度。损失函数使用Label Distribution方法进行训练,也会比直接使用交叉熵损失函数效果要好。
- 当人脸存在遮挡时,如戴眼镜,戴口罩,头发遮挡,年龄预测的误差较大,建议实际使用过程中,尽量采集正脸,无遮挡的人脸图片进行测试
- 清洗数据集(最重要):尽管鄙人已经清洗一部分了,但还是建议你,训练前,再次清洗数据集,不然会影响模型的识别的准确率。
- 增加训练的样本数据: 建议根据自己的业务场景,采集相关数据,提高模型泛化能力
- 使用参数量更大的模型: 本教程使用的是mobilenet_v2模型,属于比较轻量级的分类模型,采用更大的模型(如resnet50),理论上其精度更高,但推理速度也较慢。
- 尝试不同数据增强的组合进行训练
- 增加数据增强: 已经支持: 随机裁剪,随机翻转,随机旋转,颜色变换等数据增强方式,可以尝试诸如mixup,CutMix等更复杂的数据增强方式
- 样本均衡: 原始数据年龄类别数据并不均衡,类别20-40岁的数据偏多,而老年人和小孩的数据偏少,这会导致训练的模型会偏向于样本数较多的类别。建议进行样本均衡处理。
- 调超参: 比如学习率调整策略,优化器(SGD,Adam等)
- 损失函数: 目前训练代码已经支持:交叉熵,LabelSmoothing,可以尝试FocalLoss等损失函数
(2) 将Pytorch模型转换ONNX模型
目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行C/C++端上部署。部署流程可分为四步:训练模型->将模型转换ONNX模型->将ONNX模型转换为TNN模型->C/C++部署TNN模型。
训练好Pytorch模型后,我们需要先将模型转换为ONNX模型,以便后续模型部署。
- 原始项目提供转换脚本,你只需要修改model_file为你模型路径即可
- convert_torch_to_onnx.py实现将Pytorch模型转换ONNX模型的脚本
python libs/convert/convert_torch_to_onnx.py
"""
This code is used to convert the pytorch model into an onnx format model.
"""
import sys
import os
sys.path.insert(0, os.getcwd())
import argparse
from demo import Predictor
from basetrainer.utils import log, setup_config
from basetrainer.utils.converter import pytorch2onnx
def get_parser():
# 配置文件
config_file = "../../configs/config.yaml"
# 模型文件
# model_file = "../../work_space/AE_mobilenet_v2_1.0_L1Loss_20240103_191147_2420/model/best_model_073_0.8107.pth"
model_file = "../../work_space/AE_mobilenet_v2_1.0_L1Loss_20240105_181151_9813/model/best_model_082_0.8021.pth"
parser = argparse.ArgumentParser(description="Inference Argument")
parser.add_argument("-c", "--config_file", help="configs file", default=config_file, type=str)
parser.add_argument("-m", "--model_file", help="model_file", default=model_file, type=str)
parser.add_argument("--use_age_ld", help="use age label distribution", default=1, type=int)
parser.add_argument("--device", help="cuda device id", default="cuda:0", type=str)
return parser
def convert_torch_to_onnx(cfg):
p = Predictor(cfg=cfg)
model = p.model
w, h = cfg.input_size
input_shape = (1, 3, h, w)
onnx_file = str(cfg.model_file).replace(".pth", ".onnx")
pytorch2onnx.convert2onnx(model,
input_shape=input_shape,
input_names=['input'],
output_names=['age', 'gender'],
onnx_file=onnx_file,
opset_version=9)
if __name__ == "__main__":
parser = get_parser()
print(parser.parse_args())
cfg = setup_config.parser_config(parser.parse_args(), cfg_updata=False)
convert_torch_to_onnx(cfg)
(3) 将ONNX模型转换为TNN模型
目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行C/C++端上部署
TNN转换工具:
- (1)将ONNX模型转换为TNN模型,请参考TNN官方说明:TNN/onnx2tnn.md at master · Tencent/TNN · GitHub
- (2)一键转换,懒人必备:一键转换 Caffe, ONNX, TensorFlow 到 NCNN, MNN, Tengine (可能存在版本问题,这个工具转换的TNN模型可能不兼容,建议还是自己build源码进行转换,2022年9约25日测试可用)
模型转换成功后,会得到两个TNN模型,一个描述模型结构的*.tnnproto的文件,一个是模型参数*.tnnmodel文件;下载到本地,后面C++/Android端上部署,需要加载*.tnnproto和*.tnnmodel文件进行模型推理。
4.年龄性别预测和识别模型C/C++部署
项目IDE开发工具使用CLion,相关依赖库主要有OpenCV,base-utils以及TNN和OpenCL(可选),其中OpenCV必须安装,OpenCL用于模型加速,base-utils以及TNN已经配置好,无需安装;
项目仅在Ubuntu18.04进行测试,Windows系统下请自行配置好开发环境。
(1)项目结构
(2)配置开发环境(OpenCV+OpenCL+base-utils+TNN)
项目IDE开发工具使用CLion,相关依赖库主要有OpenCV,base-utils以及TNN和OpenCL(可选),其中OpenCV必须安装,OpenCL用于模型加速,base-utils以及TNN已经配置好,无需安装;
项目仅在Ubuntu18.04进行测试,Windows系统下请自行配置和编译
- 安装OpenCV:图像处理
图像处理(如读取图片,图像裁剪等)都需要使用OpenCV库进行处理
安装教程:Ubuntu18.04安装opencv和opencv_contrib
OpenCV库使用opencv-4.3.0版本,opencv_contrib库暂时未使用,可不安装
- 安装OpenCL:模型加速
安装教程:Ubuntu16.04 安装OpenCV&OpenCL
OpenCL用于模型GPU加速,若不使用OpenCL进行模型推理加速,纯C++推理模型,速度会特别特别慢
- base-utils:C++库
GitHub:https://github.com/PanJinquan/base-utils (无需安装,项目已经配置了)
base_utils是个人开发常用的C++库,集成了C/C++ OpenCV等常用的算法
- TNN:模型推理
GitHub:https://github.com/Tencent/TNN (无需安装,项目已经配置了)
由腾讯优图实验室开源的高性能、轻量级神经网络推理框架,同时拥有跨平台、高性能、模型压缩、代码裁剪等众多突出优势。TNN框架在原有Rapidnet、ncnn框架的基础上进一步加强了移动端设备的支持以及性能优化,同时借鉴了业界主流开源框架高性能和良好拓展性的特性,拓展了对于后台X86, NV GPU的支持。手机端 TNN已经在手机QQ、微视、P图等众多应用中落地,服务端TNN作为腾讯云AI基础加速框架已为众多业务落地提供加速支持。
(3)部署TNN模型
项目实现了C/C++版本的车牌检测和车牌识别,车牌检测模型YOLOv5和车牌识别模型PlateNet,模型推理采用TNN部署框架(支持多线程CPU和GPU加速推理);图像处理采用OpenCV库,模型加速采用OpenCL,在普通设备即可达到实时处理。
如果你想在这个 Demo部署你自己训练的车牌检测模型YOLOv5和车牌识别模型PlateNet,你可将训练好的Pytorch模型转换ONNX ,再转换成TNN模型,然后把原始的模型替换成你自己的TNN模型即可。
(4)CMake配置
这是CMakeLists.txt,其中主要配置OpenCV+OpenCL+base-utils+TNN这四个库,Windows系统下请自行配置和编译
cmake_minimum_required(VERSION 3.5)
project(Detector)
add_compile_options(-fPIC) # fix Bug: can not be used when making a shared object
set(CMAKE_CXX_FLAGS "-Wall -std=c++11 -pthread")
#set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
#set(CMAKE_CXX_FLAGS_DEBUG "-g")
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
# -DCMAKE_BUILD_TYPE=Debug
# -DCMAKE_BUILD_TYPE=Release
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (default Debug)" FORCE)
endif ()
# opencv set
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS} ./src/)
#MESSAGE(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
# base_utils
set(BASE_ROOT 3rdparty/base-utils) # 设置base-utils所在的根目录
add_subdirectory(${BASE_ROOT}/base_utils/ base_build) # 添加子目录到build中
include_directories(${BASE_ROOT}/base_utils/include)
include_directories(${BASE_ROOT}/base_utils/src)
MESSAGE(STATUS "BASE_ROOT = ${BASE_ROOT}")
# TNN set
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
# build for platform
# set(TNN_BUILD_SHARED OFF CACHE BOOL "" FORCE)
if (CMAKE_SYSTEM_NAME MATCHES "Android")
set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_ARM_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_BUILD_SHARED OFF CACHE BOOL "" FORCE)
set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE) # Multi-Thread
#set(TNN_HUAWEI_NPU_ENABLE OFF CACHE BOOL "" FORCE)
add_definitions(-DTNN_OPENCL_ENABLE) # for OpenCL GPU
add_definitions(-DTNN_ARM_ENABLE) # for Android CPU
add_definitions(-DDEBUG_ANDROID_ON) # for Android Log
add_definitions(-DPLATFORM_ANDROID)
elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_CPU_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_X86_ENABLE OFF CACHE BOOL "" FORCE)
set(TNN_QUANTIZATION_ENABLE OFF CACHE BOOL "" FORCE)
set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE) # Multi-Thread
add_definitions(-DTNN_OPENCL_ENABLE) # for OpenCL GPU
add_definitions(-DDEBUG_ON) # for WIN/Linux Log
add_definitions(-DDEBUG_LOG_ON) # for WIN/Linux Log
add_definitions(-DDEBUG_IMSHOW_OFF) # for OpenCV show
add_definitions(-DPLATFORM_LINUX)
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_CPU_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_X86_ENABLE ON CACHE BOOL "" FORCE)
set(TNN_QUANTIZATION_ENABLE OFF CACHE BOOL "" FORCE)
set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE) # Multi-Thread
add_definitions(-DTNN_OPENCL_ENABLE) # for OpenCL GPU
add_definitions(-DDEBUG_ON) # for WIN/Linux Log
add_definitions(-DDEBUG_LOG_ON) # for WIN/Linux Log
add_definitions(-DDEBUG_IMSHOW_OFF) # for OpenCV show
add_definitions(-DPLATFORM_WINDOWS)
endif ()
set(TNN_ROOT 3rdparty/TNN)
include_directories(${TNN_ROOT}/include)
include_directories(${TNN_ROOT}/third_party/opencl/include)
add_subdirectory(${TNN_ROOT}) # 添加外部项目文件夹
set(TNN -Wl,--whole-archive TNN -Wl,--no-whole-archive)# set TNN library
MESSAGE(STATUS "TNN_ROOT = ${TNN_ROOT}")
# Detector
include_directories(src)
set(SRC_LIST
src/classification.cpp
src/object_detection.cpp
src/face_alignment.cpp
src/Interpreter.cpp
)
add_library(dlcv SHARED ${SRC_LIST})
target_link_libraries(dlcv ${OpenCV_LIBS} base_utils)
MESSAGE(STATUS "DIR_SRCS = ${SRC_LIST}")
add_executable(Detector src/main.cpp)
target_link_libraries(Detector dlcv ${TNN} -lpthread)
(5)main源码
主程序中函数main实现提供了年龄性别预测和识别的使用方法,支持图片,视频和摄像头测试
- test_image_file(); // 测试图片文件
- test_video_file(); // 测试视频文件
- test_camera(); //测试摄像头
(6)源码编译和运行
编译脚本,或者直接:bash build.sh
#!/usr/bin/env bash
if [ ! -d "build/" ];then
mkdir "build"
else
echo "exist build"
fi
cd build
cmake ..
make -j4
sleep 1
./demo
- 如果你要测试CPU运行的性能,请修改src/main.cpp
DeviceType device = CPU;
- 如果你要测试GPU运行的性能,请修改src/main.cpp (需配置好OpenCL)
DeviceType device = GPU;
PS:纯CPU C++推理模式比较耗时,需要几秒的时间,而开启OpenCL加速后,GPU模式耗时仅需十几毫秒,性能极大的提高。
(7)Demo测试效果
C++版本与Python版本的结果几乎是一致,下面是年龄性别预测和识别效果展示
5.项目源码下载
C/C++项目源码下载地址:C/C++实现年龄性别预测和识别(含源码,可实时预测)
整套项目源码内容包含:
- 提供C/C++版本的人脸检测模型
- 提供C/C++版本的年龄性别预测和识别项目源码,源码可用于二次开发
- C++源码支持CPU和GPU,开启GPU(OpenCL)可以实时检测和识别(纯CPU推理速度很慢,模型加速需要配置好OpenCL,GPU推理约15ms左右)
- 项目配置好了base-utils和TNN,而OpenCV和OpenCL需要自行编译安装
- C/C++ Demo支持图片,视频,摄像头测试
如果你需要年龄性别预测和识别训练代码,请参考本人另一篇博文《Pytorch实现年龄性别预测和识别(含训练代码和数据)》;
目前已经实现Android版本的年龄性别预测和识别,详细项目请参考:
年龄性别预测和识别Android APP Demo体验:https://download.csdn.net/download/guyuealian/88743711