随着摄像头的不断改进,实时物体检测功能越来越受到人们的青睐。从自动驾驶汽车、智能监控系统到增强现实应用,这项技术被广泛应用于各种场合。
计算机视觉是利用摄像头和计算机来执行上述操作的技术的高级术语,是一个庞大而复杂的领域。不过,你可能不知道,你可以在浏览器中轻松地开始实时物体检测。
本文将介绍如何使用 React 构建实时对象检测应用程序并将其部署到服务器。实时对象检测应用程序利用了用户的网络摄像头反馈。
准备
以下是本指南中使用的关键技术的细分:
- React:React 用于构建应用程序的用户界面(UI)。React 擅长呈现动态内容,在浏览器中呈现网络摄像头画面和检测到的对象时非常有用。
- TensorFlow.js:TensorFlow.js 是一个 JavaScript 库,可将机器学习的强大功能带入浏览器。它允许你加载预先训练好的物体检测模型,并直接在浏览器中运行,无需复杂的服务器端处理。
- Coco SSD:该应用程序使用一种名为 Coco SSD 的预训练物体检测模型,这是一种轻量级模型,能够实时识别大量日常物体。虽然 Coco SSD 是一个功能强大的工具,但需要注意的是,它是在一个通用的物体数据集上训练出来的。如果您有特定的检测需求,可以按照本指南使用 TensorFlow.js 训练一个自定义模型。
建立新的 React 项目
- 创建一个新的 React 项目。请运行以下命令:
npm create vite@latest kinsta-object-detection --template react
这将使用 vite 为您搭建一个基线 React 项目。
- 接下来,在项目中运行以下命令安装 TensorFlow 和 Coco SSD 库:
npm i @tensorflow-models/coco-ssd @tensorflow/tfjs
现在,您可以开始开发应用程序了。
配置应用程序
在编写对象检测逻辑代码之前,让我们先了解一下本指南中的开发内容。下面是应用程序的用户界面:
应用程序的用户界面设计
当用户点击 “Start Webcam” 按钮时,系统会提示用户授予应用程序访问网络摄像头画面的权限。权限授予后,应用程序开始显示网络摄像头画面,并检测画面中的物体。然后,该程序会渲染一个方框,在实时画面上显示检测到的对象,并添加一个标签。
首先,在 App.jsx 文件中粘贴以下代码,创建应用程序的用户界面:
import ObjectDetection from './ObjectDetection'; function App() { return ( <div className="app"> <h1>Image Object Detection</h1> <ObjectDetection /> </div> ); } export default App;
该代码段指定了页面的页眉,并导入了一个名为 ObjectDetection
的自定义组件。该组件包含捕捉网络摄像头画面和实时检测对象的逻辑。
要创建该组件,请在 src 目录中新建一个名为 ObjectDetection.jsx 的文件,并粘贴以下代码:
import { useEffect, useRef, useState } from 'react'; const ObjectDetection = () => { const videoRef = useRef(null); const [isWebcamStarted, setIsWebcamStarted] = useState(false) const startWebcam = async () => { // TODO }; const stopWebcam = () => { // TODO }; return ( <div className="object-detection"> <div className="buttons"> <button onClick={isWebcamStarted ? stopWebcam : startWebcam}>{isWebcamStarted ? "Stop" : "Start"} Webcam</button> </div> <div className="feed"> {isWebcamStarted ? <video ref={videoRef} autoPlay muted /> : <div />} </div> </div> ); }; export default ObjectDetection;
上面的代码定义了一个 HTML 结构,其中包含一个用于启动和停止网络摄像头馈送的按钮和一个 <video>
元素,一旦网络摄像头馈送激活,该元素将用于向用户显示网络摄像头馈送。状态容器 isWebcamStarted
用于存储网络摄像头的状态。startWebcam
和 stopWebcam
这两个函数用于启动和停止网络摄像头馈送。让我们来定义它们:
以下是 startWebcam
函数的代码:
const startWebcam = async () => { try { setIsWebcamStarted(true) const stream = await navigator.mediaDevices.getUserMedia({ video: true }); if (videoRef.current) { videoRef.current.srcObject = stream; } } catch (error) { setIsWebcamStarted(false) console.error('Error accessing webcam:', error); } };
该函数负责请求用户授予网络摄像头访问权限,一旦授予权限,它就会设置 <video>
以向用户显示实时网络摄像头馈送。
如果代码无法访问网络摄像头(可能是由于当前设备上没有网络摄像头或用户被拒绝),函数将向控制台打印一条信息。您可以使用错误块向用户显示失败的原因。
接下来,用以下代码替换 stopWebcam
函数:
const stopWebcam = () => { const video = videoRef.current; if (video) { const stream = video.srcObject; const tracks = stream.getTracks(); tracks.forEach((track) => { track.stop(); }); video.srcObject = null; setPredictions([]) setIsWebcamStarted(false) } };
这段代码会检查 <video>
对象访问的正在运行的视频流轨迹,并停止每个轨迹。最后,它会将 isWebcamStarted
状态设置为 false
。
此时,请尝试运行应用程序,检查是否可以访问和查看网络摄像头画面。
确保将以下代码粘贴到 index.css 文件中,以确保应用程序的外观与您之前看到的预览效果相同:
#root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; min-width: 100vw; min-height: 100vh; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } a { font-weight: 500; color: #646cff; text-decoration: inherit; } a:hover { color: #535bf2; } body { margin: 0; display: flex; place-items: center; min-width: 100vw; min-height: 100vh; } h1 { font-size: 3.2em; line-height: 1.1; } button { border-radius: 8px; border: 1px solid transparent; padding: 0.6em 1.2em; font-size: 1em; font-weight: 500; font-family: inherit; background-color: #1a1a1a; cursor: pointer; transition: border-color 0.25s; } button:hover { border-color: #646cff; } button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { :root { color: #213547; background-color: #ffffff; } a:hover { color: #747bff; } button { background-color: #f9f9f9; } } .app { width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: column; } .object-detection { width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; .buttons { width: 100%; display: flex; justify-content: center; align-items: center; flex-direction: row; button { margin: 2px; } } div { margin: 4px; } }
此外,请删除 App.css 文件,以免弄乱组件的样式。现在,您可以编写在应用程序中集成实时对象检测的逻辑了。
设置实时对象检测
- 首先,在 ObjectDetection.jsx 的顶部添加 Tensorflow 和 Coco SSD 的导入项:
import * as cocoSsd from '@tensorflow-models/coco-ssd'; import '@tensorflow/tfjs';
- 接下来,在
ObjectDetection
组件中创建一个状态,用于存储 Coco SSD 模型生成的预测数组:const [predictions, setPredictions] = useState([]);
- 接下来,创建一个函数来加载 Coco SSD 模型、收集视频源并生成预测结果:
const predictObject = async () => { const model = await cocoSsd.load(); model.detect(videoRef.current).then((predictions) => { setPredictions(predictions); }) .catch(err => { console.error(err) }); };
该函数使用视频源,对视频源中出现的物体进行预测。它将为您提供一个预测对象数组,每个对象都包含一个标签、一个置信度百分比和一组显示该对象在视频帧中位置的坐标。
您需要不断调用该函数来处理不断出现的视频帧,然后使用存储在
predictions
状态中的预测结果在实时视频画面中为每个已识别的对象显示方框和标签。 - 接下来,使用
setInterval
函数连续调用该函数。您还必须在用户停止网络摄像头馈送后停止调用该函数。为此,请使用 JavaScript 中的clearInterval
函数。在ObjectDetection
组件中添加以下状态容器和useEffect
钩子,以设置在启用网络摄像头时持续调用predictObject
函数,并在禁用网络摄像头时移除该函数:const [detectionInterval, setDetectionInterval] = useState() useEffect(() => { if (isWebcamStarted) { setDetectionInterval(setInterval(predictObject, 500)) } else { if (detectionInterval) { clearInterval(detectionInterval) setDetectionInterval(null) } } }, [isWebcamStarted])
这将使应用程序每 500 毫秒检测网络摄像头前的物体。您可以根据自己对物体检测速度的要求来改变这个值,但要注意,如果检测频率过高,可能会导致应用程序占用浏览器的大量内存。
- 现在,您已经在
prediction
状态容器中获得了预测数据,可以用它在实时视频源中的对象周围显示标签和方框。为此,请更新ObjectDetection
的return
语句,使其返回如下内容:return ( <div className="object-detection"> <div className="buttons"> <button onClick={isWebcamStarted ? stopWebcam : startWebcam}>{isWebcamStarted ? "Stop" : "Start"} Webcam</button> </div> <div className="feed"> {isWebcamStarted ? <video ref={videoRef} autoPlay muted /> : <div />} {/* Add the tags below to show a label using the p element and a box using the div element */} {predictions.length > 0 && ( predictions.map(prediction => { return <> <p style={{ left: `${prediction.bbox[0]}px`, top: `${prediction.bbox[1]}px`, width: `${prediction.bbox[2] - 100}px` }}>{prediction.class + ' - with ' + Math.round(parseFloat(prediction.score) * 100) + '% confidence.'}</p> <div className={"marker"} style={{ left: `${prediction.bbox[0]}px`, top: `${prediction.bbox[1]}px`, width: `${prediction.bbox[2]}px`, height: `${prediction.bbox[3]}px` }} /> </> }) )} </div> {/* Add the tags below to show a list of predictions to user */} {predictions.length > 0 && ( <div> <h3>Predictions:</h3> <ul> {predictions.map((prediction, index) => ( <li key={index}> {`${prediction.class} (${(prediction.score * 100).toFixed(2)}%)`} </li> ))} </ul> </div> )} </div> );
这将在网络摄像头画面的右下方显示预测列表,并使用 Coco SSD 提供的坐标在预测对象周围绘制一个方框,同时在方框顶部添加一个标签。
- 要正确设置方框和标签的样式,请在 index.css 文件中添加以下代码:
.feed { position: relative; p { position: absolute; padding: 5px; background-color: rgba(255, 111, 0, 0.85); color: #FFF; border: 1px dashed rgba(255, 255, 255, 0.7); z-index: 2; font-size: 12px; margin: 0; } .marker { background: rgba(0, 255, 0, 0.25); border: 1px dashed #fff; z-index: 1; position: absolute; } }
至此,应用程序开发完成。现在可以重新启动开发服务器来测试应用程序。下面是应用程序完成后的样子:
使用网络摄像头实时检测物体的演示。
您可以在此 GitHub 代码库中找到完整代码。
将完成的应用程序部署到服务器
最后一步是将应用程序部署到服务器(以 Kinsta 为例),让用户可以使用。
Git 仓库准备就绪后,请按照以下步骤将对象检测应用程序部署到 Kinsta:
- 登录或创建账户,查看 MyKinsta 面板。
- 使用 Git 提供商授权 Kinsta。
- 单击左侧边栏上的 Static Sites,然后单击 Add site。
- 选择要部署的版本库和分支。
- 为网站指定一个唯一的名称。
- 按以下格式添加构建设置:
构建命令:yarn build
或npm run build
- 节点版本:
20.2.0
- 发布目录:
dist
- 最后,点击 Create site。
应用程序部署完成后,您可以从仪表板单击 Visit Site 来访问应用程序。现在,您可以尝试在装有摄像头的各种设备上运行该应用程序,看看它的性能如何。
小结
您已经使用 React 和 TensorFlow.js 成功构建了一个实时对象检测应用程序。这将使您能够探索令人兴奋的计算机视觉世界,并直接在用户浏览器中创建交互式体验。
请记住,我们使用的 Coco SSD 模型只是一个起点。通过进一步探索,您可以使用 TensorFlow.js 深入研究自定义对象检测,从而定制应用程序以识别与您的需求相关的特定对象。
评论留言