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

news2024/11/29 2:34:17

文章目录

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

介绍

这篇文章我将硬核介绍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.ts

import { 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.ts

import { 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.ts

import { 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视频处理还具有很大的优化空间,例如,集成视频的编辑能力、音效等等。

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

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

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

相关文章

以客户为中心:消费电子行业的产品研发之道

在消费电子行业这片快速变化的领域中&#xff0c;产品的迭代更新和技术的创新是推动行业不断前进的动力。然而&#xff0c;随着市场的日益成熟和消费者需求的多样化&#xff0c;如何确保产品能够满足目标用户的需求&#xff0c;成为摆在每一个产品研发团队面前的难题。本文将探…

理解JVM内存模型与Java内存模型(JMM)

理解JVM内存模型与Java内存模型&#xff08;JMM&#xff09; 在Java程序的运行过程中&#xff0c;内存管理和线程的同步是两个重要的概念。本文将深入探讨JVM内存模型&#xff08;Java Virtual Machine Memory Model&#xff09;和JMM&#xff08;Java Memory Model&#xff0…

PyTorch学习5:Logistic回归

文章目录 前言一、分类问题简介二、示例1.示例步骤2.示例代码 总结 前言 介绍利用PyTorch实现Logistic回归的分类问题 一、分类问题简介 分类问题的输出为属于每一个类别的概率&#xff0c;概率值最大的即为所属类别。最常见的Sigmoid函数&#xff1a;Logistic函数。 二、示…

了解Java内存模型(Java Memory Model, JMM)

了解Java内存模型&#xff08;Java Memory Model, JMM&#xff09; Java内存模型&#xff08;Java Memory Model, JMM&#xff09;是Java语言规范中规定的一组规则&#xff0c;定义了多线程程序中变量&#xff08;包括实例字段、静态字段和数组元素&#xff09;的访问方式。JM…

树莓派4b安装宝塔面板

1、打开命令窗口&#xff0c;执行如下命令 #更新 sudo apt-get update sudo apt-get upgrade #切换root权限 sudo su root #安装宝塔面板 wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && bash install.sh安装过程有点久&#xff0c;会持…

备份树莓派系统的多种方法,构建镜像

在我们使用树莓派进行学习或者搭建实验环境时经常会把系统玩坏&#xff0c;辛苦配置的开发环境又得重新配置&#xff1b;或者更新某一软件后发现新版本和某些组件不兼容&#xff0c;又无法降级。这个时候我们会想将系统在稳定时进行备份&#xff0c;在系统出现问题后可以很方便…

在AMD GPU上加速大型语言模型的Flash Attention

Accelerating Large Language Models with Flash Attention on AMD GPUs — ROCm Blogs 引言 在这篇博客文章中&#xff0c;我们将指导您如何在AMD GPU上安装Flash Attention&#xff0c;并提供与在PyTorch中标准SDPA比较其性能的基准测试。我们还将测量Hugging Face中多个大型…

locale本地化库学习

std::locale 类型的对象&#xff08;本地环境对象&#xff09;是不可变刻面的一个不可变索引集。C 输入/输出库的每个流对象都与一个 std::locale 对象关联&#xff0c;并用它的各刻面来分析及格式化所有数据。另外&#xff0c;每个 std::basic_regex 对象也都与一个本地环境对…

【Gitlab】Gitlab MAC M1通过Docker Desktop安装教程

一、拉取镜像 docker pull yrzr/gitlab-ce-arm64v8 二、配置容器 2.1 配置Volumes 镜像下载完成后&#xff0c;可在Docker Desktop看到镜像 点击run&#xff0c;弹出以下界面&#xff0c;配置端口映射和目录挂载后&#xff0c;即可生成一个容器 配置Volumes Host PathCont…

51单片机-独立按键控制灯灯灯

目录 简介: 一. 1个独立按钮控制一个灯例子 二. 在加一个独立按键,控制第二个灯 三. 第一个开关 开灯, 第二个开关关灯 四. 点一下开灯,在点一下关灯 五. 总结 简介: 51 单片机具有强大的控制能力&#xff0c;而独立按键则提供了一种简单的输入方式。 当把独立按键与 …

Go微服务: 分布式之通过本地消息实现最终一致性和最大努力通知方案

通过本地消息实现最终一致性 1 &#xff09;概述 我们的业务场景是可以允许我们一段时间有不一致的消息的状态的&#xff0c;并没有说必须特别高的这个消息的一致性比如说在TCC这个架构中&#xff0c;如果采用了消息的最终一致性&#xff0c;整体架构设计要轻松好多即便我们库…

设计模式-外观(门面)模式(结构型)

外观模式 外观模式又称门面模式&#xff08;结构型模式&#xff09;&#xff0c;它是一个可以屏蔽系统复杂性的设计模式。俗话说没有什么问题是加一层“介质”解决不了的&#xff0c;如果有那就在加一层。在开发过程中肯定封装过Utils类&#xff0c;我认为这就是一种门面模式&…

Python魔法之旅-魔法方法(23)

目录 一、概述 1、定义 2、作用 二、应用场景 1、构造和析构 2、操作符重载 3、字符串和表示 4、容器管理 5、可调用对象 6、上下文管理 7、属性访问和描述符 8、迭代器和生成器 9、数值类型 10、复制和序列化 11、自定义元类行为 12、自定义类行为 13、类型检…

【C++11数据结构与算法】C++ 栈

C 栈(stack) 文章目录 C 栈(stack)栈的基本介绍栈的算法运用单调栈实战题LC例题&#xff1a;[321. 拼接最大数](https://leetcode.cn/problems/create-maximum-number/)LC例题&#xff1a;[316. 去除重复字母](https://leetcode.cn/problems/remove-duplicate-letters/) 栈的基…

如何使用ERC-20与Sui Coin标准创建Token

区块链使用tokens作为传递价值的基本手段。它们可以是区块链的原生交换单位&#xff0c;也可以是应用中的交换单位&#xff0c;甚至可以在游戏世界中用作货币。tokens还支持Sui和其他区块链上的强大DeFi活动。 以太坊使用ERC-20标准来创建tokens&#xff0c;借用智能合约&…

VueRouter3学习笔记

文章目录 1&#xff0c;入门案例2&#xff0c;一些细节高亮效果非当前路由会被销毁 3&#xff0c;嵌套路由4&#xff0c; 传递查询参数5&#xff0c;命名路由6&#xff0c;传递路径参数7&#xff0c;路径参数转props8&#xff0c;查询参数转props9&#xff0c;replace模式10&am…

ChatGPT-4o, 腾讯元宝,通义千问对比测试中文文化

国内的大模型应用我选择了国内综合实力最强的两个&#xff0c;一个是腾讯元宝&#xff0c;一个是通义千问。其它的豆包&#xff0c;Kimi&#xff0c;文心一言等在某些领域也有强于竞品的表现。 问一个中文文化比较基础的问题,我满以为中文文化chatGPT不如国内的大模型。可事实…

【安装笔记-20240608-Linux-动态域名更新服务之YDNS】

安装笔记-系列文章目录 安装笔记-20240608-Linux-动态域名更新服务之YDNS 文章目录 安装笔记-系列文章目录安装笔记-20240608-Linux-动态域名更新服务之YDNS 前言一、软件介绍名称&#xff1a;YDNS主页官方介绍 二、安装步骤测试版本&#xff1a;openwrt-23.05.3-x86-64注册填…

基于协调过滤算法商品推荐系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品管理&#xff0c;论坛管理&#xff0c;商品资讯管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0c;商品资讯&#xff0c;商家&#xff0c;商品 开发系统…

PyTorch学习6:多维特征输入

文章目录 前言一、模型说明二、示例1.求解步骤2.示例代码 总结 前言 介绍了如何处理多维特征的输入问题 一、模型说明 多维问题分类模型 二、示例 1.求解步骤 1.载入数据集&#xff1a;数据集用路径D:\anaconda\Lib\site-packages\sklearn\datasets\data下的diabetes.cs…