【Modelground】个人AI产品MVP迭代平台(4)——Mediapipe视频处理网站介绍

本文主要是介绍【Modelground】个人AI产品MVP迭代平台(4)——Mediapipe视频处理网站介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 介绍
  • 模型配置
  • 输入输出
  • 核心实现(源码)
  • 总结

介绍

这篇文章我将硬核介绍Modelground的第一个产品——Mediapipe视频处理!网站入口为https://tryiscool.space/ml-video/,如图所示,欢迎体验。

在这里插入图片描述
tip: 由于服务器带宽较小,初次加载模型需要一定的等待时间。

Mediapipe视频处理的目标是:在线生成Mediapipe各类模型处理后的视频,支持导出视频和自定义样式。 效果如下图所示。

Mediapipe视频处理界面

整个网站分为两部分,左侧为模型选择器和模型配置模块,右侧上部分为视频流选择及导出选项,右侧下部分为视频处理效果。

模型配置

目前支持5个模型,投篮命中识别模型、脸部识别、姿态识别、手部识别和对象检测。

在这里插入图片描述
模型参数配置,暴露出模型的所有参数,供使用者自行调整,找出最佳配置。另外,每个模型提供了部分可选预训练模型,体积不同,效果也不一样。具体可参考官网。

在这里插入图片描述
样式配置可以对模型的输出做自定义的配置,例如脸部识别结果包括左眼、左眉毛、左瞳孔、嘴唇、脸廓等,你都可以修改配置。

在这里插入图片描述

输入输出

输入有两种形式,一种是视频/图片文件,另一种是摄像头。

下方左侧是视频处理性能监控组件。下载格式分为webm/gif,默认webm,如果选择gif,录制时间不宜太长,否则gif文件过大。视频速率是视频原速度的倍数,默认原速度(1倍),可调整。背景图开关决定输出图片/视频是否包含原图/视频。

在这里插入图片描述
点击开始识别后,可点击结束识别,输出结果即可下载。以下是一个简短的人脸识别gif结果。是不是挺酷炫?

在这里插入图片描述

核心实现(源码)

首先,前一章我们说过,Modelground的工程架构是monorepo多项目架构。其公共模型库被提取到了单独的项目中,供其余项目通过workspace的方式直接引用,而不需要再发包。那么这个公共模型库,我命名为mediapipe-model-core。(当然,这个npm包是publish过的,不过近期并没有更新,也没有很好的说明文档,后期如果大家感兴趣,我可以继续维护,但请告知我。)其项目结构如下图所示。

在这里插入图片描述
封装后的模型放在model文件夹下,base.ts是模型的抽象基类,所有具体模型都继承该基类,代码如下:

// model/bast.tsimport { FilesetResolver } from "@mediapipe/tasks-vision";
import _cloneDeep from "lodash-es/cloneDeep";
import { getModelName } from ".";
import { IParams, WasmFileset } from "./type";let vision: WasmFileset;/*** 封装模型基类*/
export abstract class MediaPipeModal {public _modelInstance: any;public params: any[] = [];public config: Record<string, unknown> = {};public styleConfig: Record<string, unknown> = {};public model: any;constructor() {}/*** 初始化及加载模型*/async init(baseUrl: string = "/") {this.initConfig(baseUrl);const vision = await this.initVison(baseUrl);this._modelInstance = await this.model.createFromOptions(vision,this.config);}// wasm只加载一次async initVison(baseUrl: string) {if (vision) {return vision;}vision = await FilesetResolver.forVisionTasks(baseUrl + "wasm");return vision;}getOptions() {return _cloneDeep(this.config);}getStyle() {return _cloneDeep(this.styleConfig);}/*** 参数初始化*/initConfig(baseUrl: string) {this.params.forEach((item) => {if (item.name === "modelAssetPath") {this.config.baseOptions = {[item.name]: baseUrl + item.default,};} else {this.config[item.name] = item.default;}});}initParams() {console.log("init params");}/*** 参数修改* @param config*/async setOptions(config: any) {this.config = { ...this.config, ...config };await this._modelInstance.setOptions(this.config);this.initParams();}setStyle(config: any) {this.styleConfig = _cloneDeep(config);localStorage.setItem(getModelName(), JSON.stringify(this.styleConfig));}detect(image: HTMLImageElement) {return this._modelInstance.detect(image);}detectForVideo(video: HTMLVideoElement, timestamp: number) {return this._modelInstance.detectForVideo(video, timestamp);}/*** 子类实现各自模型的输出处理*/abstract processResults(image: HTMLImageElement | undefined,canvas: HTMLCanvasElement,res: any,options: IParams): void;abstract processVideoResults(video: HTMLVideoElement | undefined,canvas: HTMLCanvasElement,res: any,options: IParams): void;
}

由于Mediapipe模型是通过url加载的,而npm包无法发包成可访问的静态资源,因此具体的模型,都由各个引用项目自行提供,只需要在init时传入模型路径即可。

子类以人脸识别为例,代码如下:

// model/face-land-marker/index.tsimport { MediaPipeModal } from "../base";
import { FaceLandmarker, DrawingUtils } from "@mediapipe/tasks-vision";
import { IParams } from "../type";/*** @see https://developers.google.com/mediapipe/solutions/vision/face_landmarker*/
export class FaceLandMarker extends MediaPipeModal {// 模型名static modelName = "FaceLandMarker";// 模型列表modelPath = [{path: "models/FaceLandMarker/face_landmarker.task",size: 3758596,},];// 模型描述description = "人脸特征识别(face_landmarker)";// 模型参数params = [{name: "minFaceDetectionConfidence",type: "float",range: [0, 1],default: 0.5,},{name: "minFacePresenceConfidence",type: "float",range: [0, 1],default: 0.5,},{name: "minTrackingConfidence",type: "float",range: [0, 1],default: 0.5,},{name: "numFaces",type: "integer",range: [0, 5],default: 1,},{name: "outputFaceBlendshapes",type: "boolean",range: [true, false],default: false,},{name: "outputFacialTransformationMatrixes",type: "boolean",range: [true, false],default: false,},{name: "modelAssetPath",type: "enum",range: this.modelPath.map((item) => item.path),default: this.modelPath[0].path,},{name: "runningMode",type: "enum",range: ["IMAGE", "VIDEO"],default: "VIDEO",},];// 识别结果样式styleConfig = {FACE_LANDMARKS_TESSELATION: { color: "#C0C0C070", lineWidth: 1 },FACE_LANDMARKS_RIGHT_EYE: { color: "#FF3030" },FACE_LANDMARKS_RIGHT_EYEBROW: { color: "#FF3030" },FACE_LANDMARKS_LEFT_EYE: { color: "#30FF30" },FACE_LANDMARKS_LEFT_EYEBROW: { color: "#30FF30" },FACE_LANDMARKS_FACE_OVAL: { color: "#E0E0E0" },FACE_LANDMARKS_LIPS: { color: "#E0E0E0" },FACE_LANDMARKS_RIGHT_IRIS: { color: "#FF3030" },FACE_LANDMARKS_LEFT_IRIS: { color: "#30FF30" },};constructor() {super();this.model = FaceLandmarker;const storage = localStorage.getItem(FaceLandMarker.modelName);this.styleConfig = storage ? JSON.parse(storage) : this.styleConfig;}processResults(image: HTMLImageElement | HTMLVideoElement | undefined,canvas: HTMLCanvasElement,res: any,options: IParams) {const ctx = canvas.getContext("2d")!;ctx.clearRect(0, 0, canvas.width, canvas.height);if (!!options?.renderImage) {ctx.drawImage(image!, 0, 0, canvas.width, canvas.height);}const drawingUtils = new DrawingUtils(ctx);for (const landmarks of res.faceLandmarks) {drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_TESSELATION,this.styleConfig.FACE_LANDMARKS_TESSELATION);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_RIGHT_EYE,this.styleConfig.FACE_LANDMARKS_RIGHT_EYE);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_RIGHT_EYEBROW,this.styleConfig.FACE_LANDMARKS_RIGHT_EYEBROW);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_LEFT_EYE,this.styleConfig.FACE_LANDMARKS_LEFT_EYE);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_LEFT_EYEBROW,this.styleConfig.FACE_LANDMARKS_LEFT_EYEBROW);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_FACE_OVAL,this.styleConfig.FACE_LANDMARKS_FACE_OVAL);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_LIPS,this.styleConfig.FACE_LANDMARKS_LIPS);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_RIGHT_IRIS,this.styleConfig.FACE_LANDMARKS_RIGHT_IRIS);drawingUtils.drawConnectors(landmarks,FaceLandmarker.FACE_LANDMARKS_LEFT_IRIS,this.styleConfig.FACE_LANDMARKS_LEFT_IRIS);}}processVideoResults(video: HTMLVideoElement | undefined,canvas: HTMLCanvasElement,res: any,options: IParams) {this.processResults(video, canvas, res, options);}
}

引用也十分简单,mediapipe-model-core导出了setModelName、getModelName和getModelIns三个方法,代码如下:

// model/index.tsimport { FaceLandMarker } from "./face-land-marker";
import { PoseLandMarker } from "./pose-land-marker";
import { HandLandMarker } from "./hand-land-marker";
import { BasketballGoalDetection } from "./basketball-goal-detection";
import { ObjectDetection } from "./object-detection";export * from "./basketball-goal-detection";
export * from "./face-land-marker";
export * from "./hand-land-marker";
export * from "./object-detection";
export * from "./pose-land-marker";/*** 支持的模型列表*/
export const ModelList = {[BasketballGoalDetection.modelName]: BasketballGoalDetection,[FaceLandMarker.modelName]: FaceLandMarker,[PoseLandMarker.modelName]: PoseLandMarker,[HandLandMarker.modelName]: HandLandMarker,[ObjectDetection.modelName]: ObjectDetection,
};const modelInstance: Record<string,| PoseLandMarker| FaceLandMarker| HandLandMarker| ObjectDetection| BasketballGoalDetection
> = {};let modelName: string = PoseLandMarker.modelName;let baseUrl: string = "/";/*** 获取加载好的模型实例* @returns 模型实例*/
export async function getModelIns() {if (!modelInstance[modelName]) {modelInstance[modelName] = new ModelList[modelName]();await modelInstance[modelName].init(baseUrl);}return modelInstance[modelName];
}/*** 获取当前的模型名称* @returns 模型名称*/
export function getModelName() {return modelName;
}/*** 切换当前模型* @param name 模型名称*/
export async function setModelName(name: string, url: string = "/") {baseUrl = url || "/";if (Object.keys(ModelList).includes(name)) {modelName = name;const model = await getModelIns();model.initParams();} else {console.error("未知模型名");}
}

以上都是mediapipe-model-core仓库的核心代码,那么其他项目要引用这个包,如何使用呢?
以人脸检测模型为例,具体用法如下:

import { FaceLandMarker, setModelName } from 'mediapipe-model-core';async function init() {// 设置模型await setModelName(FaceLandMarker.modelName, '/');// 获取模型实例const model = await getModelIns();// 模型识别const res = model.detectForVideo(video, startTimeMs);// 模型结果渲染await model.processVideoResults(video, canvas, res);
}

其中,设置模型需要在比较早的生命周期就调用,例如vue的mounted,因为模型的加载时间较久。

后续的模型识别、和结果渲染,都是基于requestAnimationFrame循环调用的,本质上,就是调用model.detectForVideo传入视频在startTimeMs时刻的图片,模型识别,结果绘制在传入的canvas上。

值得一提的是,目前mediapipe-model-core中有部分模型内部采用了web worker离屏canvas技术,渲染性能较高。此外,为了解决模型缓存问题,网站采用了service worker技术,以缓存模型。

其余技术细节我就不赘述,并不难。

总结

Mediapipe视频处理是Modelground的第一个孵化MVP产品。其最初是为了mediapipe-model-core库提供可视化效果,后期经过一定的设计,产出了这么一个不错的产品。其功能丰富,且特点鲜明:可导出处理后的视频,完全免费。

Mediapipe视频处理还具有很大的优化空间,例如,集成视频的编辑能力、音效等等。

如果你喜欢这篇文章,请给我一些收藏、点赞。你的支持是我创作的动力!
在这里插入图片描述

这篇关于【Modelground】个人AI产品MVP迭代平台(4)——Mediapipe视频处理网站介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1046216

相关文章

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应

Python实现自动化接收与处理手机验证码

《Python实现自动化接收与处理手机验证码》在移动互联网时代,短信验证码已成为身份验证、账号注册等环节的重要安全手段,本文将介绍如何利用Python实现验证码的自动接收,识别与转发,需要的可以参考下... 目录引言一、准备工作1.1 硬件与软件需求1.2 环境配置二、核心功能实现2.1 短信监听与获取2.

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语