简单实现一个虚拟形象系统

news2025/8/12 19:33:41

前言

上周启动居家开会的时候,看到有人通过「虚拟形象」功能,给自己带上了口罩、眼镜之类,于是想到了是不是也可以搞一个简单的虚拟形象系统。

大致想来,分为以下几个部分:
在这里插入图片描述

卷积神经网络(CNN)

下面讲解一下三层CNN网络模型:
在这里插入图片描述

卷积层——提取特征

卷积层的运算过程如下图,用一个卷积核扫完整张图片:
请添加图片描述
通过动图能够更好的理解卷积过程,使用一个卷积核(过滤器)来过滤图像的各个小区域,从而得到这些小区域的特征值。

在具体应用中,往往有多个卷积核,每个卷积核代表了一种图像模式(特征规则),如果某个图像块与此卷积核卷积出的值大,则认为此图像块十分接近于此卷积核。如果有N个卷积核,那么就认为图像中有N种底层纹理(特征),即用这N种基础纹理就能描绘出一副图像。

总结:卷积层的通过卷积核的过滤提取出图片中局部的特征。

可以看到上图卷积后,存在边缘数据特征提取减少,通常会采用边缘填充的方式,减少特征丢失。
在这里插入图片描述

池化层(下采样)——数据降维,避免过拟合

池化层通常也被叫做下采样,目的是降低数据的维度,减少数据处理量。其过程大致如下:
请添加图片描述
上图输入时是20×20的,先进行卷积采样,卷积核为10×10,采用最大池化的方式,输出为一个2×2大小的特征图。这样可将数据维度减少了10倍,方便后续模块处理。

总结:池化层相比卷积层可以更有效的降低数据维度,不仅可减少运算量,还可以避免过拟合。

过拟合是指训练误差和测试误差之间的差距太大。换句换说,就是模型复杂度高于实际问题,模型在训练集上表现很好,但在测试集上却表现很差。模型对训练集"死记硬背"(记住了不适用于测试集的训练集性质或特点),没有理解数据背后的规律,泛化能力差。
在这里插入图片描述

全连接层——输出结果

全链接层是将我们最后一个池化层的输出连接到最终的输出节点上。假设,上述CNN的最后一个池化层的输出大小为 [5×5×4],即 5×5×4=100 个节点。对于当前任务(仅识别🐱、🐶、🐍),我们的输出会是一个三维向量,输出层共 3 个节点,如输出[0.89, 0.1, 0.001],表示0.89的概率为猫。在实际应用中,通常全连接层的节点数会逐层递减,最终变为n维向量。

举个例子🌰

假设我们有2个检测的特征为「水平边缘」和「垂直边缘」。「垂直边缘」卷积过程如下:
在这里插入图片描述

最终结果如下:
在这里插入图片描述

face-api.js

face-api.js 是基于 tensorflow.js 实现的,内置了一些训练好的模型,这些模型应该是这个方案的核心,通过这些预先训练好的模型,我们可以直接使用而不需要自己再去标注、训练,极大的降低了成本。

主要提供的功能如下:

  • 人脸检测:获取一张或多张人脸的边界,可用于确认人脸的位置、数量和大小
  • 人脸特征检测:包含68个人脸特征点位,获取眉毛、眼睛、鼻子、嘴、嘴唇、下巴等的位置和形状
  • 人脸识别:返回人脸特征向量,可用于辨别人脸
  • 人脸表情识别:获取人脸表情特征
  • 性别和年龄检测:判断年龄和性别。其中“性别”是判断人脸的女性化或男性化偏向,与真实性别不一定挂钩

人脸检测

针对人脸检测,face-api 提供了 SSD Mobilenet V1 和 The Tiny Face Detector 两个人脸检测模型:

  • SSD Mobilenet V1:能够计算图像中每个人脸的位置,并返回边界框以及每个框内包含人脸的概率,但是这个模型有 5.4M,加载需要比较长的时间,弱网环境下加载过于耗时。
  • The Tiny Face Detector :这个模型性能非常好,可以做实时的人脸检测,且只有190kB,但是检测准确性上不如 SSD Mobilenet V1,且在检测比较小的人脸时不太可靠。相对而言,比较适合移动端或者设备资源优先的条件下。

人脸特征检测

针对人脸特征检测, 提供了 68 点人脸特征检测模型,检测这 68 个点的作用是为了后续的人脸对齐,为后续人脸识别做准备,这里提供了两个大小的模型供选择:350kb和80kb,大的模型肯定是更准确,小的模型适合对精确度要求不高,对资源要求占用不高的场景。其输出的区域特征点区间固定如下:

区域区间
下巴[1, 16]
左眉[18, 22]
右眉[23, 27]
鼻梁[28, 31]
鼻子[32, 26]
左眼[37, 42]
右眼[43, 48]
外嘴唇[49, 60]
内嘴唇[61, 68]

在这里插入图片描述

人脸识别

经过人脸检测以及人脸对齐以后,将检测到的人脸输入到人脸识别网络进行识别,从而获得一个128维的人脸特征向量。通过计算两个向量之间的距离(余弦值),就可以判断相似度。
在这里插入图片描述
在这里插入图片描述

虚拟形象系统

获取人脸图像

WebRTC相关的知识可以阅读我的另一篇文章《【万字长文】WebRTC浅析与实战》

目前主流浏览器提供了WebRTC能力,我们可以调用getUserMedia方法指定设备采集音视频数据。其中constrains详情参考 MediaTrackConstraints - Web APIs | MDN。

const constraints = { audio: true, video: { width: 1280, height: 720 } };
const setLocalMediaStream = (mediaStream: MediaStream) => {
    videoRef.current.srcObject = mediaStream;
}
navigator
    .mediaDevices
    .getUserMedia(constraints)
    .then(setLocalMediaStream)

获取人脸特征

根据官方文档介绍,The Tiny Face Detector模型与人脸特征识别模型组合的效果更好,故本文使用的人脸检测模型是The Tiny Face Detector

这个模型有两个参数可以调整,包括 inputSizescoreThreshold,默认值是 416 和 0.5。

  • inputSize:表示检测范围(人脸边框),值越小检测越快,但是对小脸的检测准确不足,可能会检测不出,如果是针对视频的实时检测,可以设置比较小的值。

  • scoreThreshold:是人脸检测得分的阈值,假如在照片中检测不到人脸,可以将这个值调低。

  1. 首先我们要选择并加载模型(这里使用官网训练好的模型和权重参数)
// 加载人脸检测模型
await faceApi.nets.tinyFaceDetector.loadFromUri(
    'xxx/weights/',
);
// 加载特征检测模型
await faceApi.nets.faceLandmark68Net.loadFromUri(
    'xxx/weights/',
);
  1. 转换人脸检测模型。face-api的人脸检测模型默认是 SSD Mobilenet v1,这里需要显式调整为The Tiny Face Detector模型。
const options = new faceApi.TinyFaceDetectorOptions({
  inputSize,
  scoreThreshold,
});

// 人脸68点位特征集
const result = await faceApi
  .detectSingleFace(videoEl, options) // 人脸检测
  .withFaceLandmarks(); // 特征检测

形象绘制

经过上述计算,我们已经拿到了人脸68点位特征集。需要先计算点位相对坐标信息,然后进行形象绘制。

const canvas = canvasRef.current;
const canvasCtx = canvas.getContext('2d');
const dims = faceApi.matchDimensions(canvas, videoEl, true);
const resizedResult = faceApi.resizeResults(result, dims);

本文使用的是一张256*256的口罩图片,选取1号和16号点位绘制口罩,根据两点位之间的距离缩放口罩大小。
在这里插入图片描述

这里主要调研了两种方式,分别是canvas绘制和媒体流绘制。

canvas绘制

首先想到的一种方式,video和canvas大小和位置固定,定时抓取video媒体流中图片,进行识别人脸,然后绘制在canvas上。

const { positions } = resizedResult.landmarks;
const leftPoint = positions[0];
const rightPoint = positions[16];
const length = Math.sqrt(
    Math.pow(leftPoint.x - rightPoint.x, 2) +
      Math.pow(leftPoint.y - rightPoint.y, 2),
);
canvasCtx?.drawImage(
    mask,
    0,
    0,
    265,
    265,
    leftPoint.x,
    leftPoint.y,
    length,
    length,
);

媒体流绘制

canvas提供了一个api叫做 captureStream,会返回一个继承MediaStream的实例,实时视频捕获画布上的内容(媒体流)。我们可以在canvas上以固定帧率进行图像绘制,获取视频轨道。
在这里插入图片描述

这样我们仅需保证video和canvas大小一致,位置无需固定,甚至canvas可以离屏不渲染。

const stream = canvasRef.current.captureStream()!;
  mediaStream = res[0].clone();
  mediaStream.addTrack(stream.getVideoTracks()[0]);
  videoRef.current!.srcObject = mediaStream;

对比

  • canvas绘制兼容性更好,但在实时通信场景下,需传递点位信息或端重复计算,容易受网络波动以及硬件设备影响,导致实际绘制出现偏差(依赖端能力)
  • 媒体流绘制兼容性较差,但是在直播等场景下效果会更好,在输出端做好已经做好媒体流融合,接收端依托媒体能力播放即可。同时内容也不易篡改

实际效果

因为这里仅使用了2个点位的信息,所以效果一般般。我们完全可以充分利用68个点位全面换肤,实现各种效果。
在这里插入图片描述

延伸思考

  1. 测评场景下:判断人脸数量、是否是用户本人,自动提醒用户,异常状态记录日志,监控人员可以后台查看
  2. 学习场景下:判断用户是否离开屏幕等,提醒用户返回学习状态。
  3. 弹幕场景下:检测人脸,解决弹幕遮挡问题
    …(欢迎补充)

参考文档

一文看懂卷积神经网络-CNN(基本原理+独特价值+实际应用)- 产品经理的人工智能学习库
基于face-api.js实现人脸识别的实践和总结
face-api.js:在浏览器中进行人脸识别的JS接口
卷积神经网络
CNN Explainer
face-api.js
卷积神经网络 (Convolutional Neural Network, CNN) - Leo Van | 范叶亮

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频格式转换器哪个好用?万兴优转-好用的视频格式转换器

视频格式转换器是用于转换视频格式的软件,是指用于视频转换、音频转换、CD轨抓取、音视频混合转换、音视频剪切、连接转换、视频水印叠加、滚动字幕、个性化文字、图片叠加、视频相框叠加的音视频转换工具。 也就是说,视频有非常多的格式如AVI、VCD、SVC…

【JavaWeb从零到一】会话技术CookieSessionJSP

🚀【JavaWeb从零到一】系列文章目录 🚩【JavaWeb从零到一】前置知识 🚩【JavaWeb从零到一】Mysql基础总结 🚩【JavaWeb从零到一】JDBC详解 🚩【JavaWeb从零到一】JDBC连接池&JDBCTemplate Cookie&Session&…

王学岗音视频开发(二)—————OpenGLES开发实践

矩阵以及矩阵运算 上图就是m x a 的矩阵 1x30x22x1 :为左侧第一行乘以右侧第一列。 1x10x12x0 :为左侧第一行乘以右侧第二列。 -1x33x21x1:为左侧第二行乘以右侧第一列。 -1x13x11x0:为左侧第二行乘以右侧第二列。 矩阵的行列式 伴随矩阵 A*表示伴随矩阵 OpenGL 教程----屏…

Grails SpringBoot国际化不生效

问题描述: grails项目使用了国际化,按照官方文档的说法: 会根据用户浏览器访问时使用的Accept-Language头自动选择合适的语言。 但无论浏览器了配置什么语言甚至配置了Tomcat启动参数 -Duser.languagexxx -Duser.regionxxx页面显示依旧是英…

[附源码]java毕业设计一点到家小区微帮服务系统

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

QPushButton按钮用法

QPushButton 简介 QPushButton是一个很常用的一个按钮控件,主要用于创建一个可按压的按键。它显示了一 个文本和一个图标。另外,你也可以在创建时,指定一个快捷键。 基本用法 1. 创建 QPushButton主要有两种创建方法,一种是直…

SQLite实现的学生管理系统

SQLite数据库 案例资源所在地址: https://download.csdn.net/download/weixin_41957626/87150608?spm1001.2014.3001.5503 1.简介 1.1引入 1.前面学习的文件存储和SharedPreference存储的方式只能存储一些小型的数据但是对于复杂关系以及复杂数据结构的数据仅仅靠…

交互与前端16 Tabulator 表格实践4

说明 继续给表格来加一些小功能。 内容 1 分页 在表格初始化的地方加两行配置,表格就实现了分页 pagination:true, //enable.paginationSize:20, // this option can take any positive integer value2 超链接 这个需求的来源是,一些微服务需要注释,所以我写了很多文档…

科研教育「双目视觉技术」首选!维视MV-VS220双目立体视觉系统开发平台

NO.1产品背景 在最近大热的自动驾驶赛道,大疆采用新的技术路线——双目立体视觉。具体来说,它就是模拟人的视觉系统,通过两个临近摄像头所拍摄到的画面的视差,来还原出三维立体结构。不需要对海量数据进行标注和训练,可…

Echarts 散点象限图(二)动态绘制

之前发布过一篇文章Echarts散点象限图,基于死数据来绘制的,但实际开放场景中,需要请求数据,而且可能会动态更改数据,这时候需要如何处理,有什么要注意的地方,这篇文章详细说明一下。 主要需要处理的地方就是四个象限的markArea,需要根据中心的位置来画,你可以想象成…

36 - 经典问题解析三(赋值 string)

---- 整理自狄泰软件唐佐林老师课程 1. 关于赋值的疑问 什么时候需要重载赋值操作符?编译器是否提供默认的赋值操作? 编译器为每个类 默认重载了赋值操作符默认的赋值操作符 仅完成 浅拷贝当需要进行 深拷贝 时 必须重载赋值操作符赋值操作符与拷贝构造…

Elasticsearch系列【1】概述

有道无术,术尚可求,有术无道,止于术。 文章目录Elastic公司Elastic StackElasticsearch结构化/非结构化数据全文搜索ES 发展史ES 特点ES 应用场景ES 应用案例Beats 系列LogstashKibanaElastic公司 Elastic是一家以搜索引擎闻名世界的软件公司…

pythoin爬虫2之利用cookie进行登录

python爬虫2之利用cookie进行登录利用requests模拟post方法cookies的获取session综合实例拓展:实现时间间隔(第一次抓取后间隔...秒进行下一次抓取)利用requests模拟post方法 requests.post(url,data,hearders) url即要解析的网址data即是向服…

【学习笔记18】JavaScript对象的基本认识

一、什么是对象 (一)概念 😃 就是一个数据的集合(复杂数据)😁 对象属于复杂数据类型(引用数据类型) (二)知识点的补充 JS 的数据类型 1. 基本数据 2. 复杂数据(引用数据类型) 引用数…

图像处理技术:数字图像分割 ------ 图像分割、边界分割(边缘检测)、区域分割

一、图像分割概述 • 定义 是指根据灰度、彩色、空间纹理、几何形状等特征把图像划分 成若干个互不相交的区域,使得这些特征在同一区域内表现出一致 性或相似性,而在不同区域间表现出明显的不同 分割出来的区域应该同时满足: (1&#xff09…

opencv之修改尺寸、灰度转换(python)

1、修改图片大小 #修改图片大小 resize_img cv.resize(img,dsize(200,100)) 完整代码 import cv2 as cv img cv.imread("C:\\Users\\Administrator\\Desktop\\huge.jpg") #修改图片大小 resize_img cv.resize(img,dsize(200,100)) #显示修改后图片 cv.imshow(修改…

【单目标优化求解】贪婪非分级灰狼算法求解单目标优化问题(G-NHGWO)【含Matlab源码 2005期】

⛄一、贪婪随机自适应搜索算法简介 1 贪婪随机自适应搜索算法 GRASP算法分两个阶段,构造阶段和局部搜索阶段[3]。在构造阶段,初始化可行解S和候选集C,并对候选集的每一个元素进行评估,判断是否可加入限制候选列表(Res…

西电通信技术基础实验

实验一 基带传输常用编码 实验内容 数字编码技术:AMI码、曼彻斯特编码、HDB3码 实验目的 掌握几种基带传输常用码型的编码规则,如AMI码、HDB3码、曼彻斯特码。然后利用MATLAB设计并实现它们的编码。 实验环境 MATLAB程序设计、MATLAB Simulink仿真…

IMX6ULL + SPI LCD(驱动IC ILI9341)显示简单的QT界面

1. 硬件: 使用正点原子的IMX6ULL Linux开发板 开发板底板原理图版本:V2.1 核心板原理图版本:V1.6 LCD :MSP2402 (IC ILI9341) 2. 查找可用引脚 开发板上引出的引脚是在JP6上,只看JP6会发现没有可用的SPI引脚&…

【前端内容学习】vue的引用,下载,语法

&#xff08;1&#xff09;vue的使用方式 原版本应该是三种使用方式&#xff0c;但是现在vue官网好像已经找不到关于本地下载的内容了 1&#xff0c;直接在官网上引用网络CDN文件&#xff0c;注意这种引用方式&#xff0c;要把这一段写在所有使用vue的部分前面 <script s…