vlambda博客
学习文章列表

能在浏览器中运行的OpenCV | 第92期

       当我们认真检查手机中的App时,可能会发现其实经常使用的并没有几个。也就是说,对于绝大多数的App实在没有必要采用客户端的形式,而可以使用HTML5所开发的Web应用代替,这也是最近几年HTML5受到热捧的原因。

       开发和维护客户端并不是一件容易的事情,而且对于用户而言安装客户端意味着麻烦,也意味着可能允许一些无良厂商在自己的手机上胡作非为。 对于技术开发而言,使用HTML5开发Web应用这是一个很不错的选择,因为可以降低获取流量和效果反馈的门槛。图像处理或者计算机视觉类Web应用的开发者可能都有一个共同的愿望 —— 把OpenCV搬到浏览器中。

1 从C++到Javascript


       对Web开发稍有了解的朋友对Javascript (JS)都不会太陌生,如果你实在没有听说过JS的大名,那么可以想象一下在浏览器中看到的几乎所有的动态效果和功能响应都是JS运行的结果。有很多编程语言都宣称跨平台,但是恐怕没有其他的语言能够在实际使用中像JS一样如此方便地跨平台,这一切都归功于PC端和移动端浏览器的良好兼容性。

      JS是一种脚本动态语言,虽然用起来很自由,但是运行效率实在不敢恭维。对于一些需要处理繁重计算任务的应用来说,如果想要实现Web方式的访问,就得考虑拥抱WebAssembly(wasm)。 这是一种能够将C++代码转换成JS代码的编译解决方案。

从C++到Javascript

       我们都知道C语言是最接近机器码的高级语言,而C++是C语言的超集,C++程序的执行效率仅次于纯粹的C语言程序。把计算密集的核心任务交给C++来完成,把功能性的、业务型的的任务交给JS来完成,这是目前解决动态语言执行效率问题最主流的解决方案,比如Python的C扩展。

       所以,如果你有很大存量的C++代码,或者你需要使用C++加速Web应用,毫无疑问,wasm是目前的首选解决方案。

2 生成OpenCV.js


       毋庸置疑,OpenCV是目前最全最强大的开源图像处理程序库,如果你打算或者正在构建图像处理类的应用,那么使用OpenCV是理所当然的选择。事实上,除了图像处理,OpenCV在加载深度学习模型时也比其他程序库更加全面。目前,OpenCV除了能够加载其本身所训练的模型文件,还可以加载Caffe、PyTorch以及TensorFlow训练的模型文件。也就是说,OpenCV可以用于深度学习模型的部署,并且是以C++程序的执行效率在运行。

       OpenCV的主体正是使用C/C++编写的,虽然你可能正在使用Python版本或者Java版本,但是这些都只是在主体程序上套接了一种接口而已,实际在密集处理数据的还是C/C++程序。

      官方的源代码仓库中已经集成了将OpenCV构建成JS的编译配置脚本,因此并不需要使用者对wasm机制有多了解,只需按照流程和参数执行官方的编译脚本即可。该编译过程除了依赖Emscripten编译器之外,还依赖Java和Python。编译流程完全由 platforms/js/build_js.py 脚本所控制,感兴趣的朋友可以看看这个脚本文件的源代码。具体的编译流程以及编译过程中可能会遇到的问题,请参考青衣极客的博客(链接失效可以点击左下角【阅读原文】)。

能在浏览器中运行的OpenCV | 第92期

编译生成OpenCV.js

       执行完成整个编译流程之后,就可以得到我们想要的编译产出  OpenCV.js。如果你实在对编译这种东西感到恶心,并且也没有在其中增加新功能的需要,其实也可以从OpenCV的官网使用教程的源码中下载它所使用的OpenCV.js,这样也是不错的。 顺便在官网上还可以看到对应的使用教程。

        Web应用的一大好处是:你基本可以通过“查看源文件”功能学到所有你想学的东西。但是,也有一个弊端:对代码保密变得困难。不过,绝大部分的应用或者模型,能有人感兴趣就不错了。

3 使用OpenCV.js的注意事项


       在使用OpenCV.js文件时,我们当然也可以像其他的JS库那样直接通过script标签引入,但是需要注意,OpenCV.js很大。如果为了加载这个很大的JS库,导致整个HTML页面渲染发生阻塞就使得应用程序的体验非常糟糕。所以,建议采用异步的方式导入OpenCV.js, 也就是在script标签上添加 async 关键字。

        很多的开发者都不习惯看用户手册,但这并不是一件好事情,因为手册中往往包含最权威的解释,从而避免阅读源码的低效率。不过,对于OpenCV.js而言,即使你想看用户手册,那也是没有的,所以只能从官网的用户教程中看到几个有限的函数接口的用法。那么想用教程里没有的函数怎么办?这里的回答是:Just do it。JS版本与其他版本函数命名是一致的,只是命名空间全都为 cv,而不是Python中常用的 cv2, 所以不用管太多,直接用就完事了。

        还有一个最重要的注意事项:所有与OpenCV有关的程序都应当在OpenCV.js库加载完成之后再执行。 这里用的是“应当”,因为在PC端,有时没有加载完也可以先设置,但是在移动端会由于没加载完成而导致各种匪夷所思的错误。那么,我们又该如何来保证这一点呢?关键是设置一个钩子函数

cv['onRuntimeInitialized'] = ()=>{main_demo();};

其中的 main_demo 就是集合了OpenCV调用的函数。

      官网的教程是在body标签的onload事件中绑定以上的设置函数,但是这个做法并不合适。因为在HTML加载完成的时候,OpenCV.js不见得已经加载完成。而把这个钩子函数放在导入OpenCV的script标签的onload事件之中更加合理。

跨平台使用OpenCV.js

       如果你只是想了解一下把OpenCV搬到浏览器的可行性和大概流程,阅读到这里就可以了;如果你想在自己的Web应用中使用OpenCV,那么可以参考博客中的OpenPose Demo。这个demo在布局上还比较简陋,但是功能基本完整,可供学习使用。