简介:本文详细介绍如何在Vue3项目中集成tracking.js、face.js和face-api.js实现前端人脸识别及张嘴动作活体检测,包含技术选型、环境配置、核心代码实现及优化建议。
tracking.ColorTracker可快速定位肤色区域通过检测用户是否完成指定动作(如张嘴)实现基础活体验证,核心流程:
npm init vue@latest face-detection-democd face-detection-demonpm install
npm install tracking face-api.js @tensorflow/tfjs# face.js需从GitHub获取最新版npm install https://github.com/eduardolundgren/tracking.js/archive/refs/heads/master.zip
src/components/FaceDetector.vue # 主检测组件ActionIndicator.vue # 动作提示组件utils/faceUtils.js # 工具函数assets/models/ # face-api模型
<script setup>import { ref, onMounted } from 'vue'import * as faceapi from 'face-api.js'import { initFaceTracker } from '@/utils/faceUtils'const videoRef = ref(null)const isDetecting = ref(false)const mouthOpenRatio = ref(0)onMounted(async () => {await loadModels()initFaceTracker(videoRef.value, updateMouthRatio)})async function loadModels() {const MODEL_URL = '/models'await faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL)await faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)await faceapi.nets.faceExpressionNet.loadFromUri(MODEL_URL)}</script>
// utils/faceUtils.jsexport async function initFaceTracker(videoElement, callback) {const stream = await navigator.mediaDevices.getUserMedia({ video: {} })videoElement.srcObject = stream// 使用tracking.js初始化基础检测const tracker = new tracking.ObjectTracker(['face'])tracking.track(videoElement, tracker)// 叠加face-api深度检测videoElement.addEventListener('play', () => {const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')setInterval(async () => {if (!videoElement.paused) {ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height)// face-api深度检测const detections = await faceapi.detectAllFaces(videoElement,new faceapi.TinyFaceDetectorOptions()).withFaceLandmarks()if (detections.length > 0) {const mouthPoints = getMouthPoints(detections[0].landmarks)const ratio = calculateMouthRatio(mouthPoints)callback(ratio)}}}, 100)})}function getMouthPoints(landmarks) {// 获取嘴部12个特征点(48-59)return landmarks.positions.slice(48, 60)}function calculateMouthRatio(points) {const topLip = points[0].y // 上唇最上点const bottomLip = points[9].y // 下唇最下点const height = bottomLip - topLip// 横向宽度作为参考const leftCorner = points[0].xconst rightCorner = points[6].xconst width = rightCorner - leftCornerreturn height / width // 开合度比例}
// 动作判断阈值const MOUTH_OPEN_THRESHOLD = 0.15let actionValid = falselet lastValidTime = 0export function checkMouthAction(ratio) {const now = Date.now()if (ratio > MOUTH_OPEN_THRESHOLD) {if (!actionValid) {lastValidTime = nowactionValid = true}} else {if (actionValid && now - lastValidTime > 1000) {// 保持张嘴状态超过1秒视为有效动作return true}actionValid = false}return false}
// 使用requestAnimationFrame优化let lastDetectionTime = 0const DETECTION_INTERVAL = 100 // msfunction optimizedDetect(videoElement, callback) {const now = performance.now()if (now - lastDetectionTime > DETECTION_INTERVAL) {lastDetectionTime = now// 执行检测逻辑callback()}requestAnimationFrame(() => optimizedDetect(videoElement, callback))}
faceapi.TinyFaceDetectorOptions替代默认检测器
const options = new faceapi.TinyFaceDetectorOptions({scoreThreshold: 0.5,inputSize: 256})
// 组件卸载时清理资源onUnmounted(() => {const stream = videoRef.value?.srcObjectstream?.getTracks().forEach(track => track.stop())// 清除TensorFlow.js内存if (tf) tf.disposeVariables()})
初始化阶段:
检测阶段:
动作判断阶段:
结果处理阶段:
async function getCameraStream() {try {return await navigator.mediaDevices.getUserMedia({video: { width: 640, height: 480, facingMode: 'user' }})} catch (err) {// 降级方案if (err.name === 'OverconstrainedError') {return await navigator.mediaDevices.getUserMedia({ video: true })}throw err}}
<!-- FaceDetector.vue 完整示例 --><template><div class="detector-container"><video ref="videoRef" autoplay playsinline></video><canvas ref="canvasRef" class="overlay"></canvas><ActionIndicator:ratio="mouthOpenRatio"@action-complete="handleActionComplete"/><div v-if="error" class="error-message">{{ error }}</div></div></template><script setup>import { ref, onMounted, onUnmounted } from 'vue'import * as faceapi from 'face-api.js'import { initFaceTracker, checkMouthAction } from '@/utils/faceUtils'const videoRef = ref(null)const canvasRef = ref(null)const mouthOpenRatio = ref(0)const error = ref(null)let animationFrameId = nullonMounted(async () => {try {await loadModels()initFaceTracker(videoRef.value, (ratio) => {mouthOpenRatio.value = ratioif (checkMouthAction(ratio)) {// 触发动作完成事件}})} catch (err) {error.value = '初始化失败: ' + err.message}})onUnmounted(() => {if (animationFrameId) {cancelAnimationFrame(animationFrameId)}// 清理资源逻辑...})async function loadModels() {const MODEL_URL = process.env.BASE_URL + 'models'await Promise.all([faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)])}</script><style scoped>.detector-container {position: relative;width: 100%;max-width: 640px;margin: 0 auto;}video, canvas {position: absolute;top: 0;left: 0;width: 100%;height: 100%;}.overlay {pointer-events: none;}</style>
本方案通过结合tracking.js的快速定位能力和face-api.js的精确识别能力,实现了前端轻量级的人脸识别与活体检测。在实际应用中,可根据具体场景调整检测参数和动作阈值。未来可进一步探索:
通过合理的技术选型和性能优化,前端人脸识别完全可以达到实用级别的准确率和响应速度,为各类身份验证场景提供可靠的技术支持。