当前位置:   article > 正文

面部表情识别4:C++实现表情识别(含源码,可实时检测)_表情识别源代码

表情识别源代码

面部表情识别4:C++实现表情识别(含源码,可实时检测)

目录

面部表情识别4:C++实现表情识别(含源码,可实时检测)

1.面部表情识别方法

2.人脸检测方法

3.面部表情识别模型(Python)

(1) 面部表情识别模型的训练

(2) 将Pytorch模型转换ONNX模型

(3) 将ONNX模型转换为TNN模型

4.面部表情识别模型C/C++部署

(1)项目结构

 (2)配置开发环境(OpenCV+OpenCL+base-utils+TNN)

(3)部署TNN模型

(4)CMake配置

(5)main源码

(6)源码编译和运行

(7)Demo测试效果 

5.项目源码下载


这是项目《面部表情识别》系列之《C++实现表情识别(含源码,可实时检测)》,主要分享将Python训练后的面部表情识别模型(mobilenet_v2)部署到C/C++平台。我们将开发一个简易的、可实时运行的面部表情识别的C/C++ Demo。准确率还挺高的,采用轻量级mobilenet_v2模型的面部表情识别准确率也可以高达94.72%左右,基本满足业务性能需求。C/C ++版本表情识别模型推理支持CPU和GPU加速,开启GPU(OpenCL)加速,可以达到实时的检测识别效果,基本满足业务的性能需求。

先展示一下,C/C++版本的面部表情识别Demo效果(不同表情用不同的颜色框标注了)

尊重原创,转载请注明出处https://blog.csdn.net/guyuealian/article/details/129467023


更多项目《年龄性别预测》和《面部表情识别》系列文章请参考:

  1. 面部表情识别1:表情识别数据集(含下载链接)
  2. 面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)
  3. 面部表情识别3:Android实现表情识别(含源码,可实时检测)
  4. 面部表情识别4:C++实现表情识别(含源码,可实时检测)
  5. 年龄性别预测1:年龄性别数据集说明(含下载地址)https://blog.csdn.net/guyuealian/article/details/135127124
  6. 年龄性别预测2:Pytorch实现年龄性别预测和识别(含训练代码和数据)https://blog.csdn.net/guyuealian/article/details/135556789
  7. 年龄性别预测3:Android实现年龄性别预测和识别(含源码,可实时预测)https://blog.csdn.net/guyuealian/article/details/135556824
  8. 年龄性别预测4:C/C++实现年龄性别预测和识别(含源码,可实时预测)https://blog.csdn.net/guyuealian/article/details/135556843


1.面部表情识别方法

面部表情识别方法有多种实现方案,这里采用最常规的方法:基于人脸检测+面部表情分类识别方法,即先采用通用的人脸检测模型,进行人脸检测,然后裁剪人脸区域,再训练一个面部表情分类器,完成对面部表情识别;

这样做的好处,是可以利用现有的人脸检测模型,而无需重新训练人脸检测模型,可减少人工标注成本低;而人脸数据相对而言比较容易采集,分类模型可针对性进行优化。


2.人脸检测方法

本项目人脸检测训练代码请参考:https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB 

这是一个基于SSD改进且轻量化后人脸检测模型,很slim,整个模型仅仅1.7M左右,在普通Android手机都可以实时检测。人脸检测方法在网上有一大堆现成的方法可以使用,完全可以不局限我这个方法。

​​​

关于人脸检测的方法,可以参考我的博客:


3.面部表情识别模型(Python)

(1) 面部表情识别模型的训练

本篇博文不含python版本的面部表情模型以及相关训练代码,关于面部表情识别模型的训练方法,请参考本人另一篇博文《面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)》:面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)

(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
  1. """
  2. This code is used to convert the pytorch model into an onnx format model.
  3. """
  4. import sys
  5. import os
  6. sys.path.insert(0, os.getcwd())
  7. import torch.onnx
  8. import onnx
  9. from classifier.models.build_models import get_models
  10. from basetrainer.utils import torch_tools
  11. def build_net(model_file, net_type, input_size, num_classes, width_mult=1.0):
  12. """
  13. :param model_file: 模型文件
  14. :param net_type: 模型名称
  15. :param input_size: 模型输入大小
  16. :param num_classes: 类别数
  17. :param width_mult:
  18. :return:
  19. """
  20. model = get_models(net_type, input_size, num_classes, width_mult=width_mult, is_train=False, pretrained=False)
  21. state_dict = torch_tools.load_state_dict(model_file)
  22. model.load_state_dict(state_dict)
  23. return model
  24. def convert2onnx(model_file, net_type, input_size, num_classes, width_mult=1.0, device="cpu", onnx_type="default"):
  25. model = build_net(model_file, net_type, input_size, num_classes, width_mult=width_mult)
  26. model = model.to(device)
  27. model.eval()
  28. model_name = os.path.basename(model_file)[:-len(".pth")] + ".onnx"
  29. onnx_path = os.path.join(os.path.dirname(model_file), model_name)
  30. # dummy_input = torch.randn(1, 3, 240, 320).to("cuda")
  31. dummy_input = torch.randn(1, 3, input_size[1], input_size[0]).to(device)
  32. # torch.onnx.export(model, dummy_input, onnx_path, verbose=False,
  33. # input_names=['input'],output_names=['scores', 'boxes'])
  34. do_constant_folding = True
  35. if onnx_type == "default":
  36. torch.onnx.export(model, dummy_input, onnx_path, verbose=False, export_params=True,
  37. do_constant_folding=do_constant_folding,
  38. input_names=['input'],
  39. output_names=['output'])
  40. elif onnx_type == "det":
  41. torch.onnx.export(model,
  42. dummy_input,
  43. onnx_path,
  44. do_constant_folding=do_constant_folding,
  45. export_params=True,
  46. verbose=False,
  47. input_names=['input'],
  48. output_names=['scores', 'boxes', 'ldmks'])
  49. elif onnx_type == "kp":
  50. torch.onnx.export(model,
  51. dummy_input,
  52. onnx_path,
  53. do_constant_folding=do_constant_folding,
  54. export_params=True,
  55. verbose=False,
  56. input_names=['input'],
  57. output_names=['output'])
  58. onnx_model = onnx.load(onnx_path)
  59. onnx.checker.check_model(onnx_model)
  60. print(onnx_path)
  61. if __name__ == "__main__":
  62. net_type = "mobilenet_v2"
  63. width_mult = 1.0
  64. input_size = [128, 128]
  65. num_classes = 2
  66. model_file = "work_space/mobilenet_v2_1.0_CrossEntropyLoss/model/best_model_022_98.1848.pth"
  67. convert2onnx(model_file, net_type, input_size, num_classes, width_mult=width_mult)

(3) 将ONNX模型转换为TNN模型

目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行C/C++端上部署

TNN转换工具:

​​​

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_AI吃大瓜的博客-CSDN博客_opencv opencv_contrib ubuntu

OpenCV库使用opencv-4.3.0版本,opencv_contrib库暂时未使用,可不安装

  • 安装OpenCL:模型加速

 安装教程:Ubuntu16.04 安装OpenCV&OpenCL_xiaozl_284的博客-CSDN博客_clinfo源码下载

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系统下请自行配置和编译

  1. cmake_minimum_required(VERSION 3.5)
  2. project(Detector)
  3. add_compile_options(-fPIC) # fix Bug: can not be used when making a shared object
  4. set(CMAKE_CXX_FLAGS "-Wall -std=c++11 -pthread")
  5. #set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
  6. #set(CMAKE_CXX_FLAGS_DEBUG "-g")
  7. if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  8. # -DCMAKE_BUILD_TYPE=Debug
  9. # -DCMAKE_BUILD_TYPE=Release
  10. message(STATUS "No build type selected, default to Release")
  11. set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (default Debug)" FORCE)
  12. endif ()
  13. # opencv set
  14. find_package(OpenCV REQUIRED)
  15. include_directories(${OpenCV_INCLUDE_DIRS} ./src/)
  16. #MESSAGE(STATUS "OpenCV_INCLUDE_DIRS = ${OpenCV_INCLUDE_DIRS}")
  17. # base_utils
  18. set(BASE_ROOT 3rdparty/base-utils) # 设置base-utils所在的根目录
  19. add_subdirectory(${BASE_ROOT}/base_utils/ base_build) # 添加子目录到build中
  20. include_directories(${BASE_ROOT}/base_utils/include)
  21. include_directories(${BASE_ROOT}/base_utils/src)
  22. MESSAGE(STATUS "BASE_ROOT = ${BASE_ROOT}")
  23. # TNN set
  24. # Creates and names a library, sets it as either STATIC
  25. # or SHARED, and provides the relative paths to its source code.
  26. # You can define multiple libraries, and CMake buil ds it for you.
  27. # Gradle automatically packages shared libraries with your APK.
  28. # build for platform
  29. # set(TNN_BUILD_SHARED OFF CACHE BOOL "" FORCE)
  30. if (CMAKE_SYSTEM_NAME MATCHES "Android")
  31. set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
  32. set(TNN_ARM_ENABLE ON CACHE BOOL "" FORCE)
  33. set(TNN_BUILD_SHARED OFF CACHE BOOL "" FORCE)
  34. set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE) # Multi-Thread
  35. #set(TNN_HUAWEI_NPU_ENABLE OFF CACHE BOOL "" FORCE)
  36. add_definitions(-DTNN_OPENCL_ENABLE) # for OpenCL GPU
  37. add_definitions(-DTNN_ARM_ENABLE) # for Android CPU
  38. add_definitions(-DDEBUG_ANDROID_ON) # for Android Log
  39. add_definitions(-DPLATFORM_ANDROID)
  40. elseif (CMAKE_SYSTEM_NAME MATCHES "Linux")
  41. set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
  42. set(TNN_CPU_ENABLE ON CACHE BOOL "" FORCE)
  43. set(TNN_X86_ENABLE OFF CACHE BOOL "" FORCE)
  44. set(TNN_QUANTIZATION_ENABLE OFF CACHE BOOL "" FORCE)
  45. set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE) # Multi-Thread
  46. add_definitions(-DTNN_OPENCL_ENABLE) # for OpenCL GPU
  47. add_definitions(-DDEBUG_ON) # for WIN/Linux Log
  48. add_definitions(-DDEBUG_LOG_ON) # for WIN/Linux Log
  49. add_definitions(-DDEBUG_IMSHOW_OFF) # for OpenCV show
  50. add_definitions(-DPLATFORM_LINUX)
  51. elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
  52. set(TNN_OPENCL_ENABLE ON CACHE BOOL "" FORCE)
  53. set(TNN_CPU_ENABLE ON CACHE BOOL "" FORCE)
  54. set(TNN_X86_ENABLE ON CACHE BOOL "" FORCE)
  55. set(TNN_QUANTIZATION_ENABLE OFF CACHE BOOL "" FORCE)
  56. set(TNN_OPENMP_ENABLE ON CACHE BOOL "" FORCE) # Multi-Thread
  57. add_definitions(-DTNN_OPENCL_ENABLE) # for OpenCL GPU
  58. add_definitions(-DDEBUG_ON) # for WIN/Linux Log
  59. add_definitions(-DDEBUG_LOG_ON) # for WIN/Linux Log
  60. add_definitions(-DDEBUG_IMSHOW_OFF) # for OpenCV show
  61. add_definitions(-DPLATFORM_WINDOWS)
  62. endif ()
  63. set(TNN_ROOT 3rdparty/TNN)
  64. include_directories(${TNN_ROOT}/include)
  65. include_directories(${TNN_ROOT}/third_party/opencl/include)
  66. add_subdirectory(${TNN_ROOT}) # 添加外部项目文件夹
  67. set(TNN -Wl,--whole-archive TNN -Wl,--no-whole-archive)# set TNN library
  68. MESSAGE(STATUS "TNN_ROOT = ${TNN_ROOT}")
  69. # Detector
  70. include_directories(src)
  71. set(SRC_LIST
  72. src/object_detection.cpp
  73. src/classification.cpp
  74. src/Interpreter.cpp)
  75. add_library(dmcv SHARED ${SRC_LIST})
  76. target_link_libraries(dmcv ${OpenCV_LIBS} base_utils)
  77. MESSAGE(STATUS "DIR_SRCS = ${SRC_LIST}")
  78. add_executable(Detector src/main.cpp)
  79. #add_executable(Detector src/main_for_detect.cpp)
  80. #add_executable(Detector src/main_for_yolov5.cpp)
  81. target_link_libraries(Detector dmcv ${TNN} -lpthread)

(5)main源码

主程序中函数main实现提供了面部表情识别的使用方法,支持图片,视频和摄像头测试

  •     test_image_file();   // 测试图片文件
  •     test_video_file();   // 测试视频文件
  •     test_camera();       //测试摄像头
  1. //
  2. // Created by Pan on 2020/6/24.
  3. //
  4. #include "object_detection.h"
  5. #include "classification.h"
  6. #include <iostream>
  7. #include <string>
  8. #include <vector>
  9. #include "file_utils.h"
  10. #include "image_utils.h"
  11. using namespace dl;
  12. using namespace vision;
  13. using namespace std;
  14. const int num_thread = 1; // 开启CPU线程数目
  15. DeviceType device = GPU; // 选择运行设备CPU/GPU
  16. // 人脸检测模型
  17. const char *det_model_file = (char *) "../data/tnn/face/rfb-face-mask-320-320_sim.opt.tnnmodel";
  18. const char *det_proto_file = (char *) "../data/tnn/face/rfb-face-mask-320-320_sim.opt.tnnproto";
  19. ObjectDetectionParam model_param = FACE_MODEL;//模型参数
  20. // 分类模型
  21. const char *cls_model_file = (char *) "../data/tnn/emotion/mobilenet_v2_112_112.tnnmodel";
  22. const char *cls_proto_file = (char *) "../data/tnn/emotion/mobilenet_v2_112_112.tnnproto";
  23. ClassificationParam ClassParam = EMOTION_MODEL;//模型参数
  24. // 设置检测阈值
  25. const float scoreThresh = 0.5;
  26. const float iouThresh = 0.3;
  27. ObjectDetection *detector = new ObjectDetection(det_model_file,
  28. det_proto_file,
  29. model_param,
  30. num_thread,
  31. device);
  32. Classification *classifier = new Classification(cls_model_file,
  33. cls_proto_file,
  34. ClassParam,
  35. num_thread,
  36. device);
  37. /***
  38. * 测试图片文件
  39. */
  40. void test_image_file() {
  41. //测试图片的目录
  42. string image_dir = "../data/test_image";
  43. std::vector<string> image_list = get_files_list(image_dir);
  44. for (string image_path:image_list) {
  45. cv::Mat bgr_image = cv::imread(image_path);
  46. bgr_image = image_resize(bgr_image, 960);
  47. if (bgr_image.empty()) continue;
  48. FrameInfo resultInfo;
  49. // 进行人脸检测
  50. detector->detect(bgr_image, &resultInfo, scoreThresh, iouThresh);
  51. // 进行图像分类
  52. classifier->detect(bgr_image, &resultInfo);
  53. // 可视化检测结果
  54. classifier->visualizeResult(bgr_image, &resultInfo);
  55. }
  56. delete detector;
  57. detector = nullptr;
  58. delete classifier;
  59. classifier = nullptr;
  60. printf("FINISHED.\n");
  61. }
  62. /***
  63. * 测试视频文件
  64. * @return
  65. */
  66. int test_video_file() {
  67. //测试视频文件
  68. string video_file = "../data/video/video-test.mp4";
  69. cv::VideoCapture cap;
  70. bool ret = get_video_capture(video_file, cap);
  71. cv::Mat frame;
  72. while (ret) {
  73. cap >> frame;
  74. if (frame.empty()) break;
  75. FrameInfo resultInfo;
  76. // 进行人脸检测
  77. detector->detect(frame, &resultInfo, scoreThresh, iouThresh);
  78. // 进行图像分类
  79. classifier->detect(frame, &resultInfo);
  80. // 可视化检测结果
  81. classifier->visualizeResult(frame, &resultInfo, 20);
  82. }
  83. cap.release();
  84. delete detector;
  85. detector = nullptr;
  86. delete classifier;
  87. classifier = nullptr;
  88. printf("FINISHED.\n");
  89. return 0;
  90. }
  91. /***
  92. * 测试摄像头
  93. * @return
  94. */
  95. int test_camera() {
  96. int camera = 0; //摄像头ID号(请修改成自己摄像头ID号)
  97. cv::VideoCapture cap;
  98. bool ret = get_video_capture(camera, cap);
  99. cv::Mat frame;
  100. while (ret) {
  101. cap >> frame;
  102. if (frame.empty()) break;
  103. FrameInfo resultInfo;
  104. // 进行人脸检测
  105. detector->detect(frame, &resultInfo, scoreThresh, iouThresh);
  106. // 进行图像分类
  107. classifier->detect(frame, &resultInfo);
  108. // 可视化检测结果
  109. classifier->visualizeResult(frame, &resultInfo, 20);
  110. }
  111. cap.release();
  112. delete detector;
  113. detector = nullptr;
  114. delete classifier;
  115. classifier = nullptr;
  116. printf("FINISHED.\n");
  117. return 0;
  118. }
  119. int main() {
  120. test_image_file(); // 测试图片文件
  121. //test_video_file(); // 测试视频文件
  122. //test_camera(); //测试摄像头
  123. return 0;
  124. }

(6)源码编译和运行

编译脚本,或者直接:bash build.sh

  1. #!/usr/bin/env bash
  2. if [ ! -d "build/" ];then
  3. mkdir "build"
  4. else
  5. echo "exist build"
  6. fi
  7. cd build
  8. cmake ..
  9. make -j4
  10. sleep 1
  11. ./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++实现表情识别项目源码下载地址:面部表情识别4:C++实现表情识别(含源码,可实时检测)

整套项目源码内容包含:

  1. 提供C/C++版本的人脸检测模型
  2. 提供C/C++版本的面部表情分类模型
  3. C++源码支持CPU和GPU,开启GPU(OpenCL)可以实时检测和识别(纯CPU推理速度很慢,模型加速需要配置好OpenCL,GPU推理约15ms左右)
  4. 项目配置好了base-utils和TNN,而OpenCV和OpenCL需要自行编译安装
  5. C/C++ Demo支持图片,视频,摄像头测试

 Android面部表情识别APP Demo体验:https://download.csdn.net/download/guyuealian/87575425

或者链接: https://pan.baidu.com/s/16OOi-qCENP4WbIeSzO5e9g 提取码: cs5g 

如果你需要面部表情识别的训练代码,请参考:《面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)_AI吃大瓜的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/942398
推荐阅读
相关标签
  

闽ICP备14008679号