前言
WebAssembly 出来已经很久了,但是一直都没有实践过,实在是不应该,所以就趁这次国庆假期浅学一下吧。毛主席说过,“实践是检验真理的唯一标准”,所以我们今天就实现一个“视频实时滤镜效果”的功能。可直接看代码。
基本原理介绍
视频处理
我们知道,视频其实是由一幅幅的图像组成的,每一幅图像称为一“帧”。所以,我们可以通过 canvas
来获取视频的图像数据,对图像数据进行处理完后再绘制到 canvas
上去,然后通过 requestAnimationFrame
让图像动起来,代码大致如下:
1 | function draw() { |
帧率
为了衡量我们的滤镜算法效率,我们需要计算图像的帧率(FPS),大致思想是先取最近 20 次 draw
函数的平均执行时间,然后用其除 1000:
1 | function draw() { |
滤镜算法
本文采用图像处理技术中常用的卷积操作来对图像添加滤镜,卷积操作中需要使用“卷积核”,比如下面这个卷积核可以对图像起到锐化的效果:
1 | [ |
理解了卷积操作的含义后,实现一个卷积算法:
1 | function filterByJS( |
有了上面的知识做铺垫后,我们就可以实现一个 JS 版本的滤镜功能了:
不过接下来才是我们的重点,实现一个 WebAssembly 的版本。
WebAssembly 版本
首先得选一门语言,C/C++ 和 Rust 是个不错的选择,奈何臣妾实在是不会,所以只能选好学又易上手的 Golang 了。
首先,我们新建一个 Golang 项目,并添加我们的代码:
1 | package main |
Golang 部分提供了两个方法供 JS 调用,为了避免修改图像数据的时候 JS 每次都向 Golang 拷贝数据,我们这里采用共享内存的方式来传递数据,实现方法如 initShareMemory
所示。而 filterByGO
是我们的滤镜算法,其代码跟之前介绍的 JS 版类似。
然后 JS 侧就可以按照如下方式来使用:
1 | WebAssembly.instantiateStreaming(fetch('/main.wasm'), go.importObject).then( |
好了,一切就绪后,我们调试一下,结果报错了:
这个问题搜索了很久也没有得到完美的解决方案,怀疑是内存不够,将 canvas
的宽高减小后就不报错了,但是最后网页只能像这样了:
即便如此,Golang 版的 FPS 也反而还不如 JS 版的,这就有点尴尬了。
出师不利,难道是假期不适合学习?不过暂时先告一段落吧,下次试试用 Rust 实现一个。