onnx-web + yolov8n 在视频流里做推理

news2024/11/28 4:46:57

顺着我上一篇文章 使用onnxruntime-web 运行yolov8-nano推理 继续说,有朋友在问能不能接入

视频流动,实时去识别物品。

首先使用 getUserMedia 获取摄像头视频流

getUserMedia API 可以访问设备的摄像头和麦克风。你可以使用这个 API 获取视频流,并将其显示在页面上的 <video> 标签中。

注意事项:

  • 浏览器支持getUserMedia 被现代浏览器大多数支持,但在一些旧版浏览器上可能不兼容。可以使用 can I use 网站检查浏览器的支持情况。

  • HTTPS 连接:访问摄像头通常要求在 HTTPS 环境下运行。如果你在开发时遇到问题,确保你的本地开发服务器使用 HTTPS(例如通过 localhosthttps 协议)。

import React, {useState, useRef, useEffect} from "react";

const App = () => {
  const [hasVideo, setHasVideo] = useState(false);  // 用来记录是否成功获取视频流
  const videoRef = useRef(null);  // 用来引用 <video> 元素

  useEffect(() => {
    // 定义一个异步函数来获取视频流
    const getVideoStream = async () => {
      try {
        // 获取视频流
        const stream = await navigator.mediaDevices.getUserMedia({
          video: true,  // 只请求视频流,不需要音频
        });

        // 如果获取成功,将视频流绑定到 <video> 元素
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
        }

        // 标记为成功获取视频流
        setHasVideo(true);
      } catch (err) {
        console.error("Error accessing the camera: ", err);
        setHasVideo(false);
      }
    };

    // 执行获取视频流
    getVideoStream();

    // 清理函数,在组件卸载时停止视频流
    return () => {
      if (videoRef.current && videoRef.current.srcObject) {
        const stream = videoRef.current.srcObject;
        const tracks = stream.getTracks();
        tracks.forEach(track => track.stop());  // 停止视频流
      }
    };
  }, []);

  return (
      <div className="video-container">
      {hasVideo ? (
        <video ref={videoRef} autoPlay playsInline className="video-stream" />
      ) : (
        <p>Unable to access the camera.</p>
      )}
    </div>
  );

};

export default App;

效果如下, 不过我们需要实画框的并不在vedio对象上,所以需要把vedio上图片在canvas上重新绘制,方便后续在detect的位置再画上方框

增加取帧模型推理的代码

import React, {useEffect, useRef, useState} from 'react';
import "./style/Camera.css";
import cv from "@techstark/opencv-js";
import {download} from "./utils/download";
import {InferenceSession, Tensor} from "onnxruntime-web";
import {detectImage} from "./utils/detect";

const CameraToCanvas = () => {
    const videoRef = useRef(null);
    const canvasRef = useRef(null);

    const [session, setSession] = useState(null);
    const [loading, setLoading] = useState({text: "Loading OpenCV.js", progress: null});
    const imageRef = useRef(null);

    // Configs
    const modelName = "yolov8n.onnx";
    const modelInputShape = [1, 3, 640, 640];
    const topk = 100;
    const iouThreshold = 0.45;
    const scoreThreshold = 0.25;


    useEffect(() => {
        cv["onRuntimeInitialized"] = async () => {
            const baseModelURL = `${process.env.PUBLIC_URL}/model`;

            // create session
            const url = `${baseModelURL}/${modelName}`
            console.log(`url:${url}`)
            const arrBufNet = await download(url, // url
                ["加载 YOLOv8", setLoading] // logger
            );
            const yolov8 = await InferenceSession.create(arrBufNet);
            const arrBufNMS = await download(`${baseModelURL}/nms-yolov8.onnx`, // url
                ["加载 NMS model", setLoading] // logger
            );
            const nms = await InferenceSession.create(arrBufNMS);

            // warmup main model
            setLoading({text: "model 预热...", progress: null});
            const tensor = new Tensor("float32", new Float32Array(modelInputShape.reduce((a, b) => a * b)), modelInputShape);
            await yolov8.run({images: tensor});

            setSession({net: yolov8, nms: nms});
            //window.session = {net: yolov8, nms: nms};
            setLoading(null);
        };

        // 获取摄像头流
        async function startCamera() {
            try {
                const stream = await navigator.mediaDevices.getUserMedia({video: true});
                if (videoRef.current) {
                    videoRef.current.srcObject = stream;
                    videoRef.current.play();
                }
            } catch (error) {
                console.error("Error accessing the camera:", error);
            }
        }

        startCamera();

        const drawToCanvas = async () => {
            const video = videoRef.current;
            const canvas = canvasRef.current;
            const context = canvas.getContext('2d');
            if (video && canvas) {
                canvas.width = video.videoWidth;
                canvas.height = video.videoHeight;
                context.drawImage(video, 0, 0, canvas.width, canvas.height);
                // 把图片赋到imageRef.src上
                imageRef.current.src = canvas.toDataURL();
                //const session = window.session
                if (session) {
                    detectImage(imageRef.current, canvas, session, topk, iouThreshold, scoreThreshold, modelInputShape);
                }
            }
            requestAnimationFrame(drawToCanvas);
        };

        requestAnimationFrame(drawToCanvas);

        return () => {
            if (videoRef.current && videoRef.current.srcObject) {
                const stream = videoRef.current.srcObject;
                const tracks = stream.getTracks();
                tracks.forEach(track => track.stop()); // 停止摄像头流
            }
        };
    }, []);

    return (<div className='video-container'>
        <video ref={videoRef} style={{display: 'none'}}/>
        <canvas ref={canvasRef}/>
        <img
            ref={imageRef}
            src="#"
            alt=""
            style={{display: "none"}}
        />
    </div>);
}

export default CameraToCanvas;

使用模型进行检测的方法

export const detectImage = async (
    image,
    canvas,
    session,
    topk,
    iouThreshold,
    scoreThreshold,
    inputShape
) => {
    const [modelWidth, modelHeight] = inputShape.slice(2);
    const [input, xRatio, yRatio] = preprocessing(image, modelWidth, modelHeight);

    const tensor = new Tensor("float32", input.data32F, inputShape); // to ort.Tensor
    const config = new Tensor(
        "float32",
        new Float32Array([
            topk, // topk per class
            iouThreshold, // iou threshold
            scoreThreshold, // score threshold
        ])
    ); // nms config tensor
    const {output0} = await session.net.run({images: tensor}); // run session and get output layer
    const {selected} = await session.nms.run({detection: output0, config: config}); // perform nms and filter boxes

    const boxes = [];

    // looping through output
    for (let idx = 0; idx < selected.dims[1]; idx++) {
        const data = selected.data.slice(idx * selected.dims[2], (idx + 1) * selected.dims[2]); // get rows
        const box = data.slice(0, 4);
        const scores = data.slice(4); // classes probability scores
        const score = Math.max(...scores); // maximum probability scores
        const label = scores.indexOf(score); // class id of maximum probability scores

        const [x, y, w, h] = [
            (box[0] - 0.5 * box[2]) * xRatio, // upscale left
            (box[1] - 0.5 * box[3]) * yRatio, // upscale top
            box[2] * xRatio, // upscale width
            box[3] * yRatio, // upscale height
        ]; // keep boxes in maxSize range

        boxes.push({
            label: label,
            probability: score,
            bounding: [x, y, w, h], // upscale box
        }); // update boxes to draw later
    }

    if (boxes.length > 0) {
        renderBoxes(canvas, boxes); // Draw boxes
    }
    input.delete(); // delete unused Mat
};

测试下效果,视频流的处理还是卡顿,勉强找了个静态场景测试下

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

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

相关文章

Python练习11

Python日常练习 题目&#xff1a; 编写一个石头剪刀布游戏&#xff0c;该程序要求完成如下功能&#xff1a; (1) 显示游戏规则&#xff0c;提醒用户输入一个1-3的整数或者直接回车。 用户输入回车时游戏结束。 用户输入不合法&#xff08;包括输入的…

航展畅想:从F35机载软件研发来看汽车车载软件研发

两款经典战机的机载软件 F-22和F-35战斗机的研制分别始于1980年代和1990年代末&#xff0c;F-22项目在1981年启动&#xff0c;主要由洛克希德马丁&#xff08;Lockheed Martin&#xff09;和波音公司&#xff08;Boeing&#xff09;合作开发&#xff0c;以满足美军“先进战术战…

实践出真知:MVEL表达式empty的坑

目录标题 背景为什么呢&#xff1f;验证下empty的含义case1case2case3 结论具体解释&#xff1a; 背景 //是否白名单 if(goodInfo.?isWhite ! empty){showList.add(["label": "是否白名单","value":["text":(goodInfo.?isWhite tr…

RPC核心实现原理

目录 一、基本原理 二、详细步骤 三、额外考虑因素 RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种计算机通信协议&#xff0c;也是一种用于实现分布式系统中不同节点之间进行通信和调用的技术。其实现原理主要可以分为以下几个步骤&…

Kaggle生物信息学挑战:酶稳定性预测大赛

背景介绍 酶的稳定性是影响其实际应用的关键因素之一。通过定点突变可以改善酶的稳定性,但实验筛选稳定性突变体的成本较高。预测突变对酶稳定性的影响,加速筛选稳定性更高的酶突变体。 概念解释 X 残基&#xff1a;假设 它用 红色表示 &#xff0c; Y 残基&#xff1a;假设…

【开发工具——依赖管理工具——Maven】

1. Maven介绍 Apache Maven 的本质是一个软件项目管理和理解工具。基于项目对象模型 (Project Object Model&#xff0c;POM) 的概念&#xff0c;Maven 可以从一条中心信息管理项目的构建、报告和文档。 对于开发者来说&#xff0c;Maven 的主要作用主要有 3 个&#xff1a; …

vue3+vite搭建脚手架项目本地运行electron桌面应用

1.搭建脚手架项目 搭建Vue3ViteTs脚手架-CSDN博客 2.创建完项目后&#xff0c;安装所需依赖包 npm i vite-plugin-electron electron26.1.0 3.根目录下创建electron/main.ts electron/main.ts /** electron/main.ts */import { app, BrowserWindow } from "electron&qu…

鸿蒙ArkTS中的获取网络数据

一、通过web组件加载网页 在C/S应用程序中&#xff0c;都有网络组件用于加载网页&#xff0c;鸿蒙ArkTS中也有类似的组件。   web组件&#xff0c;用于加载指定的网页&#xff0c;里面有很多的方法可以调用&#xff0c;虽然现在用得比较少&#xff0c;了解还是必须的。   演…

无人车之路径规划篇

无人车的路径规划是指在一定的环境模型基础上&#xff0c;给定无人车起始点和目标点后&#xff0c;按照性能指标规划出一条无碰撞、能安全到达目标点的有效路径。 一、路径规划的重要性 路径规划对于无人车的安全、高效运行至关重要。它不仅能够提高交通效率&#xff0c;减少交…

C语言心型代码解析

方法一 心型极坐标方程 爱心代码你真的理解吗 笛卡尔的心型公式&#xff1a; for (y 1.5; y > -1.5; y - 0.1) for (x -1.5; x < 1.5; x 0.05) 代码里面用了二个for循环&#xff0c;第一个代表y轴&#xff0c;第二个代表x轴 二个增加的单位不同&#xff0c;能使得…

11月7日(内网横向移动(二))

利用系统服务 SCShell SCShell是一款利用系统服务的无文件横向移动工具。与传统的创建远程服务的方法不同&#xff0c;SCShell利用提供的用户凭据&#xff0c;通过ChangeServiceConfigA API修改远程主机上的服务配置&#xff0c;将服务的二进制路径名修改为指定的程序或攻击载…

【YOLOv11[基础]】目标检测OD | 导出ONNX模型 | ONN模型推理以及检测结果可视化 | python

本文将导出YOLO11.pt模型对应的ONNX模型,并且使用ONNX模型推理以及结果的可视化。话不多说,先看看效果图吧!!! 目录 一 导出ONNX模型 二 推理及检测结果可视化 1 代码 2 效果图

力扣—不同路径(路径问题的动态规划)

文章目录 题目解析算法原理代码实现题目练习 题目解析 算法原理 状态表示 对于这种「路径类」的问题&#xff0c;我们的状态表示⼀般有两种形式&#xff1a; i. 从[i, j] 位置出发。 ii. 从起始位置出发&#xff0c;到[i, j] 位置。 这⾥选择第⼆种定义状态表⽰的⽅式&#xf…

传统RAG流程;密集检索器,稀疏检索器:中文的M3E

目录 传统RAG流程 相似性搜索中:神经网络的密集检索器,稀疏检索器 密集检索器 BGE系列模型 text-embedding-ada-002模型 M3E模型 稀疏检索器 示例一:基于TF-IDF的稀疏检索器 示例二:基于BM25的稀疏检索器 稀疏检索器的特点与优势 传统RAG流程 相似性搜索中:神经…

Javascript 获取设备信息 工具

JS获取设备信息(操作系统信息、地理位置、UUID、横竖屏状态、设备类型、网络状态、浏览器信息、生成浏览器指纹、日期、生肖、周几等) Get Device Info Online GitHub - skillnull/DeviceJs: JS获取设备信息(操作系统信息、地理位置、UUID、横竖屏状态、设备类型、网络状态、浏…

【数据仓库】

1、概述 数据仓库&#xff0c;英文名称为Data Warehouse&#xff0c;可简写为DW或DWH。 数据仓库是企业中用于集中存储和管理来自多个源的经过处理和组织的数据的系统。它为复杂的查询和分析提供了一个优化的环境&#xff0c;使得用户能够执行高级数据分析&#xff0c;以支持…

成都栩熙酷网络科技有限公司抖音小店探索

在数字经济的浪潮中&#xff0c;电商行业正以前所未有的速度蓬勃发展&#xff0c;而短视频平台的崛起更是为这一领域注入了新的活力。成都栩熙酷网络科技有限公司&#xff08;以下简称“栩熙酷”&#xff09;&#xff0c;作为这股浪潮中的佼佼者&#xff0c;凭借其敏锐的市场洞…

基于ViT的无监督工业异常检测模型汇总

基于ViT的无监督工业异常检测模型汇总 论文1&#xff1a;RealNet: A Feature Selection Network with Realistic Synthetic Anomaly for Anomaly Detection&#xff08;2024&#xff09;1.1 主要思想1.2 系统框架 论文2&#xff1a;Inpainting Transformer for Anomaly Detecti…

传输协议设计与牧村摆动(Makimoto‘s Wave)

有一条活鱼和一条死鱼&#xff0c;你准备怎么做&#xff0c;你会将活鱼红烧或将死鱼清蒸吗&#xff1f;好的食材只需要最简单的烹饪&#xff0c;不好的食材才需要花活儿。 我此前的文字几乎都在阐述一个观点&#xff0c;广域网就是那条死鱼&#xff0c;数据中心则是那条活鱼。…

基于Python的旅游景点推荐系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…