直接在浏览器中打开OpenCV(WebAssembly和WebWorker)

2020-05-08 21:18:58

我们将看看如何在浏览器上直接使用OpenCV库!为此,我们将OpenCV编译为WebAssembly,然后在WebWorker中运行它。

OpenCV是最流行的计算机视觉类库,自1999年以来一直存在!它所做的就是提供一个用户友好、高效的开发环境。它是英特尔用C和C++编写的库。

OpenCV还可以使用英特尔的嵌入式性能原语,这是英特尔特有的一组低级例程。

通过直接从浏览器运行计算机视觉算法,我们可以将成本转移到客户端设备,从而在服务器上节省大量成本。

想象一下,您想要从一张图片中获取葡萄酒标签的特征。有很多方法可以做到这一点。如果我们为我们的服务器寻找最符合人体工程学的方式,我们会在浏览器中移动部分葡萄酒标签检测逻辑。然后,当我们将请求获取到服务器时,我们只需要发送最终的向量。这样,我们可以避免在服务器上处理图像。

或者,即使它是一款供公司私人使用的嵌入式应用程序,我们也可以将所有逻辑都放在浏览器中。

我们将在React中使用Next.js框架,以简化项目的设置和使用。但是,同样的方法也可以应用于具有ANGING、Vue.js、Svelte.。或者是Vanilla.js。

一旦你填写了你的项目名称,就用纱线开发提升当地的环境。现在我们可以开始在Next.js项目中使用OpenCV了。

完成后,将您生成的文件复制到项目中,然后将其移动到/public中。

Public├──Favicon.ico├──js+│├──opencv.js└──vercel.svg。

一旦我们将OpenCV文件放在/public目录下的webAssembly中,就可以在Worker内部使用它了。

使用Worker非常重要,因为所有OpenCV函数都非常昂贵,并且会阻塞UI。使用工人不是强制性的,但强烈建议使用。

Public├──Favicon.ico├──js+│:├──cv.worker.js│:├──opencv.js└──vercel.svg。

/*此处我们将不时检查是否可以访问OpenCV*功能。如果问题已解决*Well(True)或存在超时(False),我们将在回调中返回。*/Function(callbackFn,waitTimems=30000,stepTimems=100){if(cv.Mat)callbackFn(TRUE);let timeSpentms=0;const Interval=set(()=>;{const limitReached=timeSpentms>;waitTimems;if(cv.Mat||limitReached){clearInterval(Interval);return callbackFn(!limitReached);没有这一点,就不可能与项目进行沟通。*/OnMessage=function(E){switch(e.data.msg){case';load';:{//导入WebAssembly脚本self.import Scripts(';./opencv.js';);waitForOpencv(function(SUCCESS){IF(SUCCESS)postMessage({:e.data.msg});ELSE抛出新错误(';加载OpenCV';时出错);});

好的,现在我们可以在我们的项目中创建一个与工作人员通信的服务。为此,我们将创建一个服务目录,我们将在其中放置我们的文件。

创建文件后,我们将输入以下初始代码,这将允许我们将OpenCV加载到我们的项目中:

class{/*我们将私下使用此方法与Worker通信,并*返回带有事件结果的承诺。这样,我们就可以异步调用*Worker。*/_Dispatch(Event){const{msg}=event this._status[msg]=[';loading';]this.worker.postMessage(Event)return new Promise((res,rej)=>;{let interval=set(()=>;{const status=this._status[msg]if(status[0]=';Done';)res(status[1])if(。error';)rej(status[1])if(status[0]!==';loading';){delete this._status[msg]clearInterval(Interval)},50)}}/*首先,我们将加载Worker并捕获OnMessage*和OnError事件,以始终了解我们触发的事件*的状态。**然后,我们将调用';Load&39;事件,因为我们刚刚实现了它,以便工作人员可以捕获它。*/load(){this._status={}this.worker=new worker(';/js/cv.worker.js';)//加载worker//捕获事件并保存_status对象this.worker.onmessage=e=>;this._status[e.data.msg]=[';Done';,e]this.worker.onerror=e=>;this._status[e.data.msg]=[';error';,e]return this._Dispatch({:';load';})}}//在任何地方导出相同的即时文件导出默认新CV()。

由于我们直接导出实例,因此可以将其导入到我们的页面或组件中。

既然我们已经设法在浏览器中加载了OpenCV库,我们将了解如何从库中运行一些实用程序。

当然,使用OpenCV可以做很多事情。这里我将展示一个简单的例子。然后,您的工作就是阅读官方文档并学习如何使用OpenCV。

我们将要使用的例子是一个简单的图像处理,用相机拍摄照片并将它们处理成灰度。虽然这看起来可能很简单,但这是我们第一个使用OpenCV的hello world。

IMPORT{useEffect,useRef,useState}from';action';import cv';../services/cv';//我们将处理大小限制为200px。const maxVideoSize=200/*我们要呈现的是:**1.一个视频组件,用户可以看到摄像机上有什么。**2.生成视频图像,加载OpenCV,*图像处理按钮。**3.一张画布,让我们可以捕捉视频画面并*展示给用户。*/export default function(){const[Processing,updateProcessing]=useState(False)const VideoElement=useRef(Null)const canvasEl=useRef(Null)/*在onClick事件中,我们将捕获*视频中的一个帧以将其传递给我们的服务。*/异步函数(){updateProcessing(True)const ctx=canvasEl.current.getContext(';2d';)ctx.drawImage(VideoElement.current,0,0,maxVideoSize,maxVideoSize)const image=ctx.getImageData(0,0,maxVideoSize,maxVideoSize)//加载模型等待cv.load()//处理图像const procesdImage=await cv.imageProcessing(Image)//将处理后的图像渲染到画布ctx.putImageData(proces。*/useEffect(()=>;{async function(){VideoElement.current.width=maxVideoSize VideoElement.current.high=maxVideoSize if(Navigator.mediaDevices&;&;Navigator.mediaDevices.getUserMedia){const stream=等待Navigator.mediaDevices.getUserMedia({:false,:{:';user';,{VideoElement.current.onloadedMETADATA=()=>;{Resolve(VideoElement.current)}})}const errorMessage=';此浏览器不支持视频捕获,或者此设备没有摄像头';alert(ErrorMessage)return Promise.reject(ErrorMessage)}异步函数(){const VideoLoaded=await initCamara()VideoLoaded.play()return VideoLoaded.play()。正在处理.';:';拍摄照片';})}。

class{//.此处有以前的服务代码.。/*我们将使用前面创建的_DISPATCH事件*调用带有msg和图片的postMessage作为有效负载。**感谢我们在_Dispatch中实现的功能,这将*返回经过处理的图像的承诺。*/imageProcessing(Payload){return this._Dispatch({:';imageProcessing';,payload})}}。

//.此处有以前的工人代码.。/*使用OpenCV时,我们必须使用cv.Mat(矩阵)格式的图像,*因此您必须将ImageData转换为它。*/function({msg,payload}){const img=cv.matFromImageData(Payload)let result=new cv.Mat()//这会将图像转换为灰度。cv.cvtColor(img,result,cv.COLOR_BGR2GRAY)PostMessage({msg,:imageDataFromMat(Result)})}/*Thi

虽然我们已经以一种非常简单的方式处理了图像,而且不使用OpenCV也可以做到这一点,但这就是我们使用OpenCV的hello world。它为更复杂的事情打开了大门。

我们已经了解了如何在浏览器中使用计算机视觉最常用的库。我们已经了解了如何将OpenCV编译成WebAssembly,并在Worker中使用它来避免阻塞UI以获得良好的性能。我希望即使你从未听说过这个图书馆,现在你也要试一试。

我已经将这篇文章的代码上传到了GitHub上,如果你想看一下的话。

要查看在Vue.js中实现的更复杂的示例,请看另一个repo: