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

顺着我上一篇文章 使用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);// Configsconst 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 sessionconst 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 modelsetLoading({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.sessionif (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}/><imgref={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.Tensorconst config = new Tensor("float32",new Float32Array([topk, // topk per classiouThreshold, // iou thresholdscoreThreshold, // score threshold])); // nms config tensorconst {output0} = await session.net.run({images: tensor}); // run session and get output layerconst {selected} = await session.nms.run({detection: output0, config: config}); // perform nms and filter boxesconst boxes = [];// looping through outputfor (let idx = 0; idx < selected.dims[1]; idx++) {const data = selected.data.slice(idx * selected.dims[2], (idx + 1) * selected.dims[2]); // get rowsconst box = data.slice(0, 4);const scores = data.slice(4); // classes probability scoresconst score = Math.max(...scores); // maximum probability scoresconst label = scores.indexOf(score); // class id of maximum probability scoresconst [x, y, w, h] = [(box[0] - 0.5 * box[2]) * xRatio, // upscale left(box[1] - 0.5 * box[3]) * yRatio, // upscale topbox[2] * xRatio, // upscale widthbox[3] * yRatio, // upscale height]; // keep boxes in maxSize rangeboxes.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.xdnf.cn/news/7902.html

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

相关文章

力扣题库——136.只出现一次的数字

代码实现&#xff1a; class Solution { public:int singleNumber(vector<int>& nums) {int result0;for(int num:nums){result^num;}return result;} }; 结果&#xff1a; 思路&#xff1a;这里让0和数组元素不断异或&#xff0c;因为0与一个数异或的结果是它本身…

EasyPOI使用详解

EasyPOI 简介 easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板 语言(熟悉的表达式语法),完成以前复杂的写法 文档&#xff1a;http://easypoi.mydoc.io/#categor…

JAVA设计模式之【建造者模式】

1 定义 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 2 类图 产品类&#xff08;Product&#xff09;&#xff1a;表示被创建的复杂…

智能化健身房管理:Spring Boot与Vue的创新解决方案

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

如何修改WordPress经典编辑器的默认高度?

boke112百科有一个使用WordPress搭建的小网站&#xff0c;文章内容就是几个字不到一行&#xff0c;但是每次使用经典编辑器编辑文章时&#xff0c;都觉得编辑器默认高度太高了&#xff0c;影响了我添加文章摘要和其他属性&#xff0c;有没有办法修改WordPress经典编辑器的默认高…

C#属性 Property

属性Property不是变量。 它们是由名为访问器方法来实现的一种方法。 实例属性表示的是实例的某个数据&#xff0c;通过这个数据反映实例当前的状态 静态属性表示的是类型的某个数据&#xff0c;通过这个数据反映类型当前的状态 意义&#xff1a; 防止恶意赋值(通过属性间接访问…

【力扣热题100】[Java版] 刷题笔记-121. 买卖股票的最佳时机

题目&#xff1a;121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。…

Wi-Fi7 puncturing技术增强与应用

原文关注公众号 - 无线技术栈,及时查看网络/Wi-Fi更多知识 “本文图片没有一一列出,感兴趣可以关注公众号 - 无线技术栈” “本文图片没有一一列出,感兴趣可以关注公众号 - 无线技术栈” Puncturing是一种有效的编码技术,广泛应用于无线通信中,用于在保持信号的可靠性的同…

C语言内存函数介绍和模拟实现:(memcpy,memmove,memcmp,memset)

memcpy介绍及模拟实现&#xff1a; memcpy介绍&#xff1a; void* 是指可以接受任何类型的指针。 memcpy是把从 source 指针开始之后的 num 个字节的内存拷贝到 destination 指针之后的空间。 遇到‘\0’不会停止&#xff0c;而且memcpy不可以拷贝重叠空间&#xff0c;就是说…

浏览器指纹修改指南2024 - 修改Geolocation API指纹(十一)

引言 在前几篇文章中&#xff0c;我们已经详细探讨了Geolocation API的定义、作用及其在浏览器指纹中的重要性&#xff0c;并深入分析了Chromium源码中Geolocation API的实现位置和修改方法。通过这些分析&#xff0c;我们为后续的修改工作奠定了坚实的基础。 在本篇文章中&a…

【微信小程序】基本语法

一、导入小程序 选择代码目录 项目配置文件 appid 当前小程序的 AppIDprojectname 当前小程序的项目名称 变更AppID&#xff08;视情况而定&#xff0c;如果没有开发权限时需要变更成个人的 AppID&#xff09; 二、模板语法 在页面中渲染数据时所用到的一系列语法叫做模板…

数据结构:顺序表

顺序表 顺序表的概念与结构静态顺序表动态顺序表 动态顺序表的实现SeqList.h的创建初始化动态顺序表&#xff08;LS_Init&#xff09;动态顺序表的销毁&#xff08;LS_Destry&#xff09;检查动态内存空间是否已满&#xff08;SL_CheckCapacity&#xff09;动态顺序表打印有效数…

MySQL_数据类型建表

复习&#xff1a; 我们昨天学习的知识都忘了嘛&#xff1f;如果忘了也不要担心&#xff0c;我来带大家来复习一遍吧&#xff01;&#xff01;&#xff01; 1.查看所有数据库 show databases;2.创建属于自己的数据库 create database 数据库名; 检查自己创建的数据库是…

PHP不良事件上报系统源码,医院安全不良事件管理系统,基于 vue2+element+ laravel框架开发

不良事件上报系统通过 “事前的人员知识培训管理和制度落地促进”、“事中的事件上报和跟进处理”、 以及 “事后的原因分析和工作持续优化”&#xff0c;结合预存上百套已正在使用的模板&#xff0c;帮助医院从对护理事件、药品事件、医疗器械事件、医院感染事件、输血事件、意…

在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序

如果您有 Android 设备&#xff0c;您可能会将个人和专业的重要文件保存在设备的 SD 卡上。这些文件包括照片、视频、文档和各种其他类型的文件。您绝对不想丢失这些文件&#xff0c;但当您的 SD 卡损坏时&#xff0c;数据丢失是不可避免的。 幸运的是&#xff0c;您不需要这样…

实战:看懂并分析执行计划——Nested Loops (Inner Join)

这是执行计划中 Nested Loops 的详情信息,下面将逐行解释每个字段的含义,并提供优化思路。 Nested Loops 分析 Physical Operation: Nested Loops (Inner Join) 物理操作,表示这是一个嵌套循环连接(Nested Loops),用于执行 INNER JOIN。嵌套循环通常用于小数据集的连接…

Meta Llama3用于药物发现的微调、RAG 和提示工程-LLM保姆级资料

Meta Llama3用于药物发现的微调、RAG 和提示工程的使用指南&#xff1a;LLM微调的基本概念&#xff0c;每种微调方式的深入解读&#xff0c;2种生物医药领域的Llama3的微调应用。 LLM 如何微调LLMs&#xff1f;3种微调方式&#xff0c;什么时候&#xff1f;什么情况下该使用何…

如何通过 PXE 使用 UEFI 启动 Tiny Core Linux

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

根据关键字搜索商品API返回值解析:深入解析与代码实践

在电子商务和数据集成领域&#xff0c;API&#xff08;应用程序编程接口&#xff09;扮演着至关重要的角色。通过API&#xff0c;开发者可以访问和利用平台的数据资源&#xff0c;实现自动化和智能化的数据交互。本文将探讨如何根据关键字搜索商品API的返回值进行解析&#xff…

Python http打印(http打印body)flask demo(http调试demo、http demo、http printer)

文章目录 代码解释 代码 # flask_http_printer.pyfrom flask import Flask, request, jsonify import jsonapp Flask(__name__)app.route(/printinfo, methods[POST]) def print_info():# 分隔符separator "-" * 60# 获取请求头headers request.headers# 获取 JS…