You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
移动侦测,英文翻译为“Motion detection technology”,一般也叫运动检测,常用于无人值守监控录像和自动报警。通过摄像头按照不同帧率采集得到的图像会被 CPU 按照一定算法进行计算和比较,当画面有变化时,如有人走过,镜头被移动,计算比较结果得出的数字会超过阈值并指示系统能自动作出相应的处理。——百度百科
本文将阐述如何通过 Web 技术实现简易的移动监测效果,并附上一些有意思的案例。
由上述引用语句可得出“移动监测”需要以下要素:
注:本文涉及的所有案例均基于 PC/Mac 较新版本的 Chrome / Firefox 浏览器,部分案例需配合摄像头完成,所有截图均保存在本地。
对方不想和你说话,并向你扔来一个链接:
体验链接>>
综合案例
该案例有以下两个功能:
上述案例也许并不能直接体现出『移动监测』的实际效果和原理,下面再看看这个案例。
体验链接>>
像素差异
案例的左侧是视频源,而右侧则是移动后的像素处理(像素化、判断移动和只保留绿色等)。
因为是基于 Web 技术,所以视频源采用 WebRTC,像素处理则采用 Canvas。
视频源
不依赖 Flash 或 Silverlight,我们使用 WebRTC (Web Real-Time Communications) 中的 navigator.getUserMedia() API,该 API 允许 Web 应用获取用户的摄像头与麦克风流(stream)。
示例代码如下:
对于兼容性问题,Safari 11 开始支持 WebRTC 了。具体可查看 caniuse。
像素处理
在得到视频源后,我们就有了判断物体是否移动的素材。当然,这里并没有采用什么高深的识别算法,只是利用连续两帧截图的像素差异来判断物体是否发生移动(严格来说,是画面的变化)。
截图
获取视频源截图的示例代码:
得出截图间的差异
对于两张图的像素差异,在 凹凸实验室 的 《“等一下,我碰!”——常见的2D碰撞检测》 这篇博文中所提及的“像素检测”碰撞算法是解决办法之一。该算法是通过遍历两个离屏画布(offscreen canvas)同一位置的像素点的透明度是否同时大于 0,来判断碰撞与否。当然,这里要改为『同一位置的像素点是否不同(或差异小于某阈值)』来判断移动与否。
但上述方式稍显麻烦和低效,这里我们采用
ctx.globalCompositeOperation = 'difference'
指定画布新增元素(即第二张截图与第一张截图)的合成方式,得出两张截图的差异部分。体验链接>>
示例代码:
两张图的差异
体验上述案例后,是否有种当年玩“QQ游戏《大家来找茬》”的感觉。另外,这个案例可能还适用于以下两种情况:
何时为一个“动作”
由上述“两张图像差异”的案例中可得:黑色代表该位置上的像素未发生改变,而像素越明亮则代表该点的“动作”越大。因此,当连续两帧截图合成后有明亮的像素存在时,即为一个“动作”的产生。但为了让程序不那么“敏感”,我们可以设定一个阈值。当明亮像素的个数大于该阈值时,才认为产生了一个“动作”。当然,我们也可以剔除“不足够明亮”的像素,以尽可能避免外界环境(如灯光等)的影响。
想要获取 Canvas 的像素信息,需要通过
ctx.getImageData(sx, sy, sw, sh)
,该 API 会返回你所指定画布区域的像素对象。该对象包含data
、width
、height
。其中data
是一个含有每个像素点 RGBA 信息的一维数组,如下图所示。含有 RGBA 信息的一维数组
获取到特定区域的像素后,我们就能对每个像素进行处理(如各种滤镜效果)。处理完后,则可通过
ctx.putImageData()
将其渲染在指定的 Canvas 上。示例代码:
在上述案例中,你也许会注意到画面是『绿色』的。其实,我们只需将每个像素的红和蓝设置为 0,即将 RGBA 的
r = 0; b = 0
即可。这样就会像电影的某些镜头一样,增加了科技感和神秘感。体验地址>>
示例代码:
将 RGBA 中的 R 和 B 置为 0
跟踪“移动物体”
有了明亮的像素后,我们就要找出其 x 坐标的最小值与 y 坐标的最小值,以表示跟踪矩形的左上角。同理,x 坐标的最大值与 y 坐标的最大值则表示跟踪矩形的右下角。至此,我们就能绘制出一个能包围所有明亮像素的矩形,从而实现跟踪移动物体的效果。
找出跟踪矩形的左上角和右下角
体验链接>>
示例代码:
在得到跟踪矩形的左上角和右下角的坐标值后,通过
ctx.strokeRect(x, y, width, height)
API 绘制出矩形即可。这是理想效果,实际效果请打开 体验链接
性能
缩小尺寸
在上一个章节提到,我们需要通过对 Canvas 每个像素进行处理,假设 Canvas 的宽为
640
,高为480
,那么就需要遍历640 * 480 = 307200
个像素。而在监测效果可接受的前提下,我们可以将需要进行像素处理的 Canvas 缩小尺寸,如缩小 10 倍。这样需要遍历的像素数量就降低100
倍,从而提升性能。体验地址>>
示例代码:
尺寸缩小 10 倍
定时器
我们都知道,当游戏以『每秒60帧』运行时才能保证一定的体验。但对于我们目前的案例来说,帧率并不是我们追求的第一位。因此,每 100 毫秒(具体数值取决于实际情况)取当前帧与前一帧进行比较即可。
另外,因为我们的动作一般具有连贯性,所以可取该连贯动作中幅度最大的(即“分数”最高)或最后一帧动作进行处理即可(如存储到本地或分享到朋友圈)。
延伸
至此,用 Web 技术实现简易的“移动监测”效果已基本讲述完毕。由于算法、设备等因素的限制,该效果只能以 2D 画面为基础来判断物体是否发生“移动”。而微软的 Xbox、索尼的 PS、任天堂的 Wii 等游戏设备上的体感游戏则依赖于硬件。以微软的 Kinect 为例,它为开发者提供了可跟踪最多六个完整骨骼和每人 25 个关节等强大功能。利用这些详细的人体参数,我们就能实现各种隔空的『手势操作』,如画圈圈诅咒某人。
下面几个是通过 Web 使用 Kinect 的库:
通过 Node-Kinect2 获取骨骼数据
文章至此就真的要结束了,如果你想知道更多玩法,请关注 凹凸实验室。同时,也希望大家发掘更多玩法。
参考资料
The text was updated successfully, but these errors were encountered: