基于Yolov8面部七种表情检测与识别C++模型部署

表情识别

七种表情识别是一个多学科交叉的研究领域,它结合了心理学、认知科学、计算机视觉和机器学习等学科的知识和技术。

基本概念

  • 表情的定义:表情是人们在情绪体验时面部肌肉活动的结果,是人类情感交流的基本方式之一。
  • 基本表情理论:心理学家Paul Ekman提出,人类有七种基本情绪,每种情绪都有其特定的面部表情模式。

七种基本表情

  1. 快乐:通常与积极情绪相关,特征是嘴角上扬,眼睛周围肌肉收缩。
  2. 悲伤:与失落或痛苦相关,特征是眉毛下垂,嘴角下拉。
  3. 愤怒:与愤怒或挫败相关,特征是眉毛下压,嘴唇紧闭。
  4. 惊讶:与意外或震惊相关,特征是眼睛和嘴巴张开。
  5. 恐惧:与害怕或焦虑相关,特征是眼睛瞪大,眉毛提升。
  6. 厌恶:与反感或不喜欢相关,特征是上唇提升,嘴角下拉。
  7. 轻蔑:与鄙视或不屑一顾相关,特征是嘴角一侧上扬。

应用领域

  • 心理健康监测:通过表情识别来评估个体的情绪状态。
  • 人机交互:使机器人或计算机系统能够理解和响应用户的情绪。
  • 安全监控:在安全检查中识别可疑行为或情绪异常。
  • 娱乐和媒体:在电影、游戏和虚拟现实中生成逼真的虚拟角色表情。

挑战和限制

  • 个体差异:不同人表达相同情绪的方式可能不同。
  • 文化差异:不同文化背景下,表情的表达和解读可能存在差异。
  • 环境因素:光照、遮挡和面部表情的微妙变化可能影响识别的准确性。
  • 实时性要求:在某些应用场景下,如视频监控或实时交互,对系统的处理速度有较高要求。

发展趋势

  • 多模态识别:结合面部表情以外的信息,如语音、心率等,以提高识别的准确性。
  • 迁移学习:利用预训练的深度学习模型来提高小样本学习的性能。
  • 无监督和半监督学习:减少对大量标注数据的依赖,提高模型的泛化能力。

数据集

数据集内容:RAF-DB数据集是一个大规模面部表情数据库,由315名工作人员(大学的学生和教职员工)对表情进行标注。

在对表情的选择上,从一系列表情(例如:微笑,咯咯笑声,哭泣,愤怒,害怕,害怕,恐惧,震惊,惊讶,厌恶,无表情)中,挑选出六种基本情感以及中立情感,一共7种表情进行表情标注。

数据集数量:RAF-DB数据集,包含大约3万张面部图像。除了表情标注外,对每个人脸还有5个特征点标注,人脸边界框,种族,年龄范围和性别等属性的标注。

数据集功能:表情识别、人脸检测、年龄估计

下载链接:http://www.whdeng.cn/RAF/model1.html

在这里插入图片描述

Yolov8目标检测

YOLOv8是YOLO系列的最新迭代产品,它在目标检测领域带来了一系列创新和改进。以下是YOLOv8的一些关键特点和功能:

  1. 模型结构:YOLOv8采用了新的SOTA模型,包括不同分辨率的目标检测网络和基于YOLACT的实例分割模型。它在骨干网络和Neck部分可能参考了YOLOv7 ELAN的设计思想,将YOLOv5的C3结构换成了C2f结构,并对不同尺度模型调整了不同的通道数。

  2. Head部分:与YOLOv5相比,YOLOv8的Head部分改动较大,换成了目前主流的解耦头结构,将分类和检测头分离,并且从Anchor-Based换成了Anchor-Free。

  3. Loss计算:YOLOv8采用了TaskAlignedAssigner正样本分配策略,并引入了Distribution Focal Loss,这有助于提高检测过程的准确性和效率。

  4. 数据增强:在训练过程中,YOLOv8引入了YOLOX中的最后10个epoch关闭Mosaic增强的操作,这可以有效地提升精度。

  5. 训练策略:YOLOv8的模型训练总epoch数从300提升到了500,这导致训练时间增加,但可能有助于进一步提升模型性能。

  6. 性能和速度:YOLOv8专注于保持精度与速度之间的最佳平衡,适用于各种应用领域的实时目标检测任务。

  7. 预训练模型:YOLOv8提供一系列预训练模型,以满足各种任务和性能要求,从而更容易为您的特定用例找到合适的模型。

  8. 支持的任务和模式:YOLOv8系列提供多种模型,每种模型都专门用于计算机视觉中的特定任务,如物体检测、实例分割、姿态/关键点检测等。

  9. 性能基准测试:YOLOv8可以进行性能基准测试,以评估在不同导出格式下的速度和准确性。

  10. 实战应用:有教程提供了YOLOv8在LabVIEW中的部署,包括模型的导出、图片和视频推理的实现。

环境安装

安装环境:

conda create -n yolov8 python=3.8
activate ylolv8
pip install ultralytics

转onnx

安装完成之后,分割数据进行模型训练。训练完之后把模型转成onnx,使用以下命令将YOLO模型从PyTorch导出为ONNX格式,并设置opset为12:

yolo export model=yolov8s.pt format=onnx dynamic=False opset=12

命令的含义解释如下:

yolo export: 使用YOLO导出功能、
model=yolov8s.pt: 指定PyTorch模型的路径
format=onnx: 导出为ONNX格式
dynamic=False: 关闭动态输入
opset=12: 设置ONNX模型的opset版本为12

转ncnn模型

得到onnx模型之后要转成ncnn的模型,可以使用onnx2ncnn.exe进行模型转换,也可以使用在线的模型转换工具。

模型C++推理部署

ncnn库

从官方下载以编译好的ncnn库,我这里使用的IDE是vs2022,下载对应自己的库。导入lib和include,如果想要指用GPU进行推理,则要编译vulkan库。

C++ 推理代码

#include "FacialEmotion.h"static float fast_exp(float x)
{union {uint32_t i;float f;} v{};v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);return v.f;
}static inline float sigmoid(float x)
{return 1.0f / (1.0f + fast_exp(-x));
}
static float intersection_area(const Object& a, const Object& b)
{cv::Rect_<float> inter = a.rect & b.rect;return inter.area();
}static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{int i = left;int j = right;float p = faceobjects[(left + right) / 2].prob;while (i <= j){while (faceobjects[i].prob > p)i++;while (faceobjects[j].prob < p)j--;if (i <= j){// swapstd::swap(faceobjects[i], faceobjects[j]);i++;j--;}}//     #pragma omp parallel sections{//         #pragma omp section{if (left < j) qsort_descent_inplace(faceobjects, left, j);}//         #pragma omp section{if (i < right) qsort_descent_inplace(faceobjects, i, right);}}
}static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{if (faceobjects.empty())return;qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{picked.clear();const int n = faceobjects.size();std::vector<float> areas(n);for (int i = 0; i < n; i++){areas[i] = faceobjects[i].rect.width * faceobjects[i].rect.height;}for (int i = 0; i < n; i++){const Object& a = faceobjects[i];int keep = 1;for (int j = 0; j < (int)picked.size(); j++){const Object& b = faceobjects[picked[j]];// intersection over unionfloat inter_area = intersection_area(a, b);float union_area = areas[i] + areas[picked[j]] - inter_area;// float IoU = inter_area / union_areaif (inter_area / union_area > nms_threshold)keep = 0;}if (keep)picked.push_back(i);}
}
static void generate_grids_and_stride(const int target_w, const int target_h, std::vector<int>& strides, std::vector<GridAndStride>& grid_strides)
{for (int i = 0; i < (int)strides.size(); i++){int stride = strides[i];int num_grid_w = target_w / stride;int num_grid_h = target_h / stride;for (int g1 = 0; g1 < num_grid_h; g1++){for (int g0 = 0; g0 < num_grid_w; g0++){GridAndStride gs;gs.grid0 = g0;gs.grid1 = g1;gs.stride = stride;grid_strides.push_back(gs);}}}
}static void generate_proposals(std::vector<GridAndStride> grid_strides, const ncnn::Mat& pred, float prob_threshold, std::vector<Object>& objects)
{const int num_points = grid_strides.size();const int num_class = 7;const int reg_max_1 = 16;for (int i = 0; i < num_points; i++) //out.h{const float* scores = pred.row(i) + 4 * reg_max_1;// find label with max scoreint label = -1;float score = -FLT_MAX;for (int k = 0; k < num_class; k++){float confidence = scores[k];if (confidence > score){label = k;score = confidence;}}float box_prob = sigmoid(score);if (box_prob >= prob_threshold){ncnn::Mat bbox_pred(reg_max_1, 4, (void*)pred.row(i));{ncnn::Layer* softmax = ncnn::create_layer(ncnn::layer_to_index("Softmax"));
//                ncnn::layer_to_index("Softmax")ncnn::ParamDict pd;pd.set(0, 1); // axis
//                pd.set(1, 1);softmax->load_param(pd);ncnn::Option opt;opt.num_threads = 1;opt.use_packing_layout = false;softmax->create_pipeline(opt);softmax->forward_inplace(bbox_pred, opt);softmax->destroy_pipeline(opt);delete softmax;}float pred_ltrb[4];for (int k = 0; k < 4; k++){float dis = 0.f;const float* dis_after_sm = bbox_pred.row(k);for (int l = 0; l < reg_max_1; l++){dis += l * dis_after_sm[l];}pred_ltrb[k] = dis * grid_strides[i].stride;}float pb_cx = (grid_strides[i].grid0 + 0.5f) * grid_strides[i].stride; float pb_cy = (grid_strides[i].grid1 + 0.5f) * grid_strides[i].stride;float x0 = pb_cx - pred_ltrb[0];float y0 = pb_cy - pred_ltrb[1];float x1 = pb_cx + pred_ltrb[2];float y1 = pb_cy + pred_ltrb[3];Object obj;obj.rect.x = x0;obj.rect.y = y0;obj.rect.width = x1 - x0;obj.rect.height = y1 - y0;obj.label = label;obj.prob = box_prob;objects.push_back(obj);}}
}//调用ncnn转置操作
static void transpose(const ncnn::Mat& in, ncnn::Mat& out)
{ncnn::Option opt;opt.num_threads = 1;opt.use_fp16_storage = false;opt.use_packing_layout = true;ncnn::Layer* op = ncnn::create_layer("Permute");// set paramncnn::ParamDict pd;pd.set(0, 1);// order_type=1op->load_param(pd);op->create_pipeline(opt);op->forward(in,out, opt);op->destroy_pipeline(opt);delete op;
}FacialEmotion::FacialEmotion()
{blob_pool_allocator.set_size_compare_ratio(0.f);workspace_pool_allocator.set_size_compare_ratio(0.f);
}int FacialEmotion::load(std::string parma_path,std::string bin_path,int _target_size,bool use_gpu)
{yolo.clear();blob_pool_allocator.clear();workspace_pool_allocator.clear();ncnn::set_cpu_powersave(2);ncnn::set_omp_num_threads(ncnn::get_big_cpu_count());yolo.opt = ncnn::Option();#if NCNN_VULKANyolo.opt.use_vulkan_compute = use_gpu;
#endifyolo.opt.num_threads = 2;yolo.opt.blob_allocator = &blob_pool_allocator;yolo.opt.workspace_allocator = &workspace_pool_allocator;yolo.load_param(parma_path.c_str());yolo.load_model(bin_path.c_str());target_size = _target_size;return 0;
}int FacialEmotion::detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold, float nms_threshold)
{int width = rgb.cols;int height = rgb.rows;// pad to multiple of 32int w = width;int h = height;float scale = 1.f;if (w > h){scale = (float)target_size / w;w = target_size;h = h * scale;}else{scale = (float)target_size / h;h = target_size;w = w * scale;}ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgb.data, ncnn::Mat::PIXEL_BGR, width, height, w, h);// pad to target_size rectangleint wpad = (w + 31) / 32 * 32 - w;int hpad = (h + 31) / 32 * 32 - h;ncnn::Mat in_pad;ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);in_pad.substract_mean_normalize(0, norm_vals);ncnn::Extractor ex = yolo.create_extractor();ex.input("images", in_pad);std::vector<Object> proposals;ncnn::Mat out;ex.extract("/model.22/Concat_3_output_0", out);ncnn::Mat out1;transpose(out, out1);std::vector<int> strides = {8, 16, 32}; // might have stride=64std::vector<GridAndStride> grid_strides;generate_grids_and_stride(in_pad.w, in_pad.h, strides, grid_strides);generate_proposals(grid_strides, out1, prob_threshold, proposals);qsort_descent_inplace(proposals);// apply nms with nms_thresholdstd::vector<int> picked;nms_sorted_bboxes(proposals, picked, nms_threshold);int count = picked.size();objects.resize(count);for (int i = 0; i < count; i++){objects[i] = proposals[picked[i]];// adjust offset to original unpaddedfloat x0 = (objects[i].rect.x - (wpad / 2)) / scale;float y0 = (objects[i].rect.y - (hpad / 2)) / scale;float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;// clipx0 = std::max(std::min(x0, (float)(width - 1)), 0.f);y0 = std::max(std::min(y0, (float)(height - 1)), 0.f);x1 = std::max(std::min(x1, (float)(width - 1)), 0.f);y1 = std::max(std::min(y1, (float)(height - 1)), 0.f);objects[i].rect.x = x0;objects[i].rect.y = y0;objects[i].rect.width = x1 - x0;objects[i].rect.height = y1 - y0;}// sort objects by areastruct{bool operator()(const Object& a, const Object& b) const{return a.rect.area() > b.rect.area();}} objects_area_greater;std::sort(objects.begin(), objects.end(), objects_area_greater);return 0;
}int FacialEmotion::draw(cv::Mat& rgb, const std::vector<Object>& objects)
{static const char *class_names[] = {"surprise", "fear","disgust","happiness","sadness","anger","neutral"};static const unsigned char colors[19][3] = {{ 54,  67, 244},{ 99,  30, 233},{176,  39, 156},{183,  58, 103},{181,  81,  63},{243, 150,  33},{244, 169,   3},{212, 188,   0},{136, 150,   0},{ 80, 175,  76},{ 74, 195, 139},{ 57, 220, 205},{ 59, 235, 255},{  7, 193, 255},{  0, 152, 255},{ 34,  87, 255},{ 72,  85, 121},{158, 158, 158},{139, 125,  96}};int color_index = 0;for (size_t i = 0; i < objects.size(); i++){const Object& obj = objects[i];const unsigned char* color = colors[color_index % 19];color_index++;cv::Scalar cc(color[0], color[1], color[2]);cv::rectangle(rgb, obj.rect, cc, 2);char text[256];sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);int baseLine = 0;cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);int x = obj.rect.x;int y = obj.rect.y - label_size.height - baseLine;if (y < 0)y = 0;if (x + label_size.width > rgb.cols)x = rgb.cols - label_size.width;cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)), cc, -1);cv::Scalar textcc = (color[0] + color[1] + color[2] >= 381) ? cv::Scalar(0, 0, 0) : cv::Scalar(255, 255, 255);cv::putText(rgb, text, cv::Point(x, y + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.5, textcc, 1);}return 0;
}

实现的效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
源码地址:https://download.csdn.net/download/matt45m/89612957

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/392164.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Step Functions运行AWS Backup时必备的权限要点

引言 在尝试从Step Functions执行AWS Backup的按需备份时&#xff0c;我在权限方面遇到了一些困难。为了备忘&#xff0c;我将这些经验写成这篇文章。 概述 从Step Functions执行AWS Backup时&#xff0c;需要分配以下权限&#xff1a; AWS Backup相关权限 执行备份的权限…

Java: 线程安全问题的解决方案(synchronized)

发生原因 要想解决线程安全问题,那么我们首先得知道线程安全问题为什么会发生. 发生原因: 线程在操作系统中是"随机调度,抢占式执行的"[根本原因].多个线程,同时修改同一个变量修改操作不是"原子"的内存可见性问题指令重排序 解决方案 原因1和2,我们很…

04:【stm32】LED编程和按键控制

LED编程和按键控制 1、LED编程1.1、点亮一课LED灯 2、按键控制2.1、通过一个按钮控制LED灯的亮灭 1、LED编程 1.1、点亮一课LED灯 首先&#xff0c;我们想象一下&#xff0c;让LED灯点亮&#xff0c;引脚应该是输出模式&#xff0c;那么应该是通用模式&#xff0c;还是复用模式…

打靶记录7——Hacker_Kid-v1.0.1

靶机下载地址 https://download.vulnhub.com/hackerkid/Hacker_Kid-v1.0.1.ova难度 OSCP 风格的中级难度靶机&#xff08;只需要获取root权限即可&#xff0c;CTF 风格的靶机就还需要获取flag&#xff09; 涉及的攻击方法&#xff1a; 主机发现端口扫描Web信息收集DNS区域传…

Redis2-Redis常见命令

目录 Redis数据结构介绍 Redis通用命令 KEYS DEL EXISTS EXPIRE String类型 Key的层级格式 Hash类型 List类型 Set类型 SortedSet类型 Redis数据结构介绍 Redis是一个key-value的数据库&#xff0c;key一般是String数据库&#xff0c;value的类型多种多样 可以通过…

《Unity3D网络游戏实战》学习与实践--制作一款大乱斗游戏

角色类 基类Base Human是基础的角色类&#xff0c;它处理“操控角色”和“同步角色”的一些共有功能&#xff1b;CtrlHuman类代表“操控角色”​&#xff0c;它在BaseHuman类的基础上处理鼠标操控功能&#xff1b;SyncHuman类是“同步角色”类&#xff0c;它也继承自BaseHuman&…

解决电脑缺少.NET组件?手把手教你轻松解决

在日常使用电脑的过程中&#xff0c;很多用户可能会遇到“电脑缺少.NET组件”的提示&#xff0c;这可能导致某些应用程序无法正常运行或安装。那么&#xff0c;.NET组件到底是什么&#xff1f;为何它如此重要&#xff1f;本文将为您详细解答这些问题&#xff0c;并提供有效的解…

[ACM MM 2024] Wave-Mamba:超高清暗光图像增强的小波状态空间模型

Wave-Mamba: Wavelet State Space Model for Ultra-High-Definition Low-Light Image Enhancement (arxiv.org) Wave-Mamba是一种用于增强超高清低光照图像的新模型&#xff0c;它引入了低频状态空间块和高频增强块&#xff0c;并取得了领先水平的性能。该模型即将开源&#x…

用Python插入表格到PowerPoint演示文稿

有效的信息传达是演示文稿中的重点&#xff0c;而PowerPoint演示文稿作为最广泛使用的演示工具之一&#xff0c;提供了丰富的功能来帮助演讲者实现这一目标。其中&#xff0c;在演示文稿中插入表格可以帮助观众更直观地理解数据和比较信息。通过使用Python这样的强大编程语言&a…

【STL】 vector的底层实现

1.vector的模拟代码完整实现&#xff08;后面会拆分开一个一个细讲&#xff09; #pragma once #include<assert.h>// 抓重点namespace bit {/*template<class T>class vector{public:typedef T* iterator;private:T* _a;size_t _size;size_t _capacity;};*/templa…

Python(模块)

模块编写完成就可以被其他模块进行调用并使用被调用模块的功能。 import导入方式的语法结构&#xff1a; import模块名称【as别名】 from……import导入方式的语法结构&#xff1a; from模块名称&#xff0c;import变量/函数/类/*&#xff08;*是通配符&#xff09; impor…

Milvus 向量数据库进阶系列丨构建 RAG 多租户/多用户系统 (上)

本系列文章介绍 在和社区小伙伴们交流的过程中&#xff0c;我们发现大家最关心的问题从来不是某个具体的功能如何使用&#xff0c;而是面对一个具体的实战场景时&#xff0c;如何选择合适的向量数据库解决方案或最优的功能组合。在 “Milvus 向量数据库进阶” 这个系列文章中&…

【生成式AI-二-强大的AI下我们可以做什么】

强大的AI下我们可以做什么 人工智能的厉害之处我们可以作什么评估模型好坏的难度prompt engineering微调fine tune 人工智能的厉害之处 人工智能并不是忽然就爆火的&#xff0c;事实上&#xff0c;很久以前就已经有深度学习、机器学习这些概念了&#xff0c;那现在的人工智能和…

Java之类和对象

目录 static关键字 1修饰属性 2修饰方法 final 构造方法 基本语法 this关键字 代码块 定义 普通代码块 构造代码块 静态代码块 匿名对象 toString 总结 static关键字 1修饰属性 Java的静态属性和类相关, 和具体的实例无关. 换句话说, 同一个类的不同实例共用同一个…

反转链表 II(LeetCode)

题目 给你单链表的头指针 和两个整数 和 &#xff0c;其中 。请你反转从位置 到位置 的链表节点&#xff0c;返回 反转后的链表 。 解题 class ListNode:def __init__(self, value0, nextNone):self.value valueself.next nextdef reverseBetween(head: ListNode, lef…

crm客户管理系统有哪些?盘点大家使用最广泛的15款

将对比的客户管理CRM系统包括&#xff1a;纷享销客、Zoho CRM、销售易、用友CRM、Salesforce、Microsoft Dynamics 365、销帮帮CRM、HubSpot、Oracle CRM、悟空CRM、神州云动CRM、红圈CRM、SAP CRM、Odoo、OroCRM。 一个合适的CRM系统可以极大地提高你的销售效率和客户满意度&a…

SpringMVC执行流程

1 流程对比 1.1 原生servlet开发流程 根据需求编写servlet程序在web.xml 中通过配置&#xff0c;指定servlet所能处理的请求&#xff0c;即建立servlet与请求路径间的映射在Servlet的service方法中对路径进行再判断&#xff0c;从而进行具体的逻辑处理servlet参数从request中…

数据结构-递归

用递归代替循环 假设工作中的你&#xff0c;需要写一个倒数程序。该程序接收一个数字&#xff0c;例如10&#xff0c;然后显示从10到0的数字。现在先暂停一下&#xff0c;选择一门编程语言来实现这个程序&#xff0c;做完以后&#xff0c;再往下阅读。或许你用了JavaScript&am…

数学建模--二分法

目录 二分法的基本原理 应用实例 求解方程根 查找有序数组中的元素 注意事项 Python代码示例 ​编辑 延伸 二分法在数学建模中的具体应用案例有哪些&#xff1f; 如何选择二分法的初始区间以确保收敛速度和精度&#xff1f; 在使用二分法求解方程时&#xff0c;如何…

排序算法2:直接选择排序与快速排序

目录 1.直接选择排序 1.1直接选择排序的优化 2.快速排序 2.1基准值的置位&#xff08;Hoare版&#xff09; 2.2挖坑法 2.3lomuto前后指针 前言 前面我们进入了排序算的讲解。今天我们将继续学习几种重要的排序思想&#xff0c;好&#xff0c;咱们三连上车开始今天的内容。…