이 기사에서는 주로 웹 기술이 모션 모니터링, 모션 감지(일반적으로 모션 감지라고도 함)를 구현하는 방법을 소개합니다. 이는 무인 감시 비디오 및 자동 알람에 자주 사용됩니다. 카메라가 서로 다른 프레임 속도로 수집한 이미지는 특정 알고리즘에 따라 CPU에서 계산 및 비교되며, 사람이 지나가거나 렌즈가 움직이는 등 사진이 변경되면 계산 및 비교 결과에서 얻은 숫자가 표시됩니다. 임계값을 초과하고 시스템이 자동으로 해당 처리를 수행할 수 있음을 나타냅니다.
웹 기술을 사용한 모바일 모니터링 소개
위 인용에서 "모바일 모니터링"에는 다음 요소가 필요하다는 결론을 내릴 수 있습니다.
컴퓨터 움직임 알고리즘을 결정하는 데 사용되는 카메라 움직임 후 처리
참고: 이 기사에 포함된 모든 사례는 PC/Mac의 최신 버전 Chrome/Firefox 브라우저를 기반으로 하며 일부 사례는 카메라로 완료해야 합니다. 모든 스크린샷은 로컬에 저장됩니다.
상대가 대화를 원하지 않아 링크를 던집니다:
종합 케이스
이 케이스는 다음 두 가지 기능을 가지고 있습니다.
촬영 후 POST 1초 동안 사진이 촬영되고 1초 후에 음악이 정지됩니다. 움직임이 발생하면 재생 상태가 다시 시작됩니다. 위의 경우는 "모션 모니터링"의 실제 효과와 원리를 직접적으로 반영하지 않을 수 있습니다. 이 경우에는 또.
체험링크>웹 기술을 기반으로 하기 때문에 영상 소스는 WebRTC를, 픽셀 처리는 Canvas를 사용합니다.
비디오 소스
<!-- 若不加 autoplay,则会停留在第一帧 --> <video id="video" autoplay></video> // 具体参数含义可看相关文档。 const constraints = { audio: false, video: { width: 640, height: 480 } } navigator.mediaDevices.getUserMedia(constraints) .then(stream => { // 将视频源展示在 video 中 video.srcObject = stream }) .catch(err => { console.log(err) })호환성 문제로 인해 Safari 11에서는 WebRTC를 지원하기 시작합니다. 자세한 내용은 caniuse를 참조하세요.
픽셀 처리
동영상 소스를 얻은 후 물체가 움직이는지 여부를 판단할 수 있는 자료가 있습니다. 물론 여기에는 고급 인식 알고리즘이 사용되지 않습니다. 단지 두 개의 연속 스크린샷 간의 픽셀 차이를 사용하여 개체가 이동했는지 여부를 판단합니다(엄격히 말하면 사진의 변화입니다).Screenshot
비디오 소스의 스크린샷을 얻기 위한 샘플 코드:
const video = document.getElementById('video') const canvas = document.createElement('canvas') const ctx = canvas.getContext('2d') canvas.width = 640 canvas.height = 480 // 获取视频中的一帧 function capture () { ctx.drawImage(video, 0, 0, canvas.width, canvas.height) // ...其它操作 }
스크린샷의 차이를 알아보세요두 사진의 픽셀 차이를 보려면 ""잠깐만 기다려 주세요. "" 범프랩의! "——일반적인 2D 충돌 감지" 이 블로그 게시물에서 언급된 '픽셀 감지' 충돌 알고리즘은 솔루션 중 하나입니다. 이 알고리즘은 두 개의 오프 스크린 캔버스의 동일한 위치에 있는 픽셀의 투명도가 동시에 0보다 큰지 여부를 탐색하여 충돌이 있는지 확인합니다. 물론 여기서는 "동일한 위치의 픽셀이 다른지(또는 그 차이가 특정 임계값보다 작은지)"로 변경하여 이동 여부를 판단해야 합니다.
하지만 위의 방법은 약간 번거롭고 비효율적입니다. 여기서는
를 사용하여 캔버스에 있는 새 요소(즉, 두 번째 스크린샷과 첫 번째 스크린샷)의 합성 방법을 지정하여 두 스크린샷의 차이를 알아냅니다.
체험링크>>
샘플코드 :
function diffTwoImage () { // 设置新增元素的合成方式 ctx.globalCompositeOperation = 'difference' // 清除画布 ctx.clearRect(0, 0, canvas.width, canvas.height) // 假设两张图像尺寸相等 ctx.drawImage(firstImg, 0, 0) ctx.drawImage(secondImg, 0, 0) }
ctx.globalCompositeOperation = 'difference'
두 사진의 차이점
위 사례를 경험하고 나면, "QQ게임 "틀린그림찾기"" 다시 하고 싶은 기분이 드시나요? 그 다음에. 또한 이 사례는 다음 두 가지 상황에도 해당될 수 있습니다.
想要获取 Canvas 的像素信息,需要通过 ctx.getImageData(sx, sy, sw, sh)
,该 API 会返回你所指定画布区域的像素对象。该对象包含 data
、width
、height
。其中 data
是一个含有每个像素点 RGBA 信息的一维数组,如下图所示。
含有 RGBA 信息的一维数组
获取到特定区域的像素后,我们就能对每个像素进行处理(如各种滤镜效果)。处理完后,则可通过 ctx.putImageData()
将其渲染在指定的 Canvas 上。
扩展:由于 Canvas 目前没有提供“历史记录”的功能,如需实现“返回上一步”操作,则可通过 getImageData 保存上一步操作,当需要时则可通过 putImageData 进行复原。
示例代码:
let imageScore = 0 const rgba = imageData.data for (let i = 0; i < rgba.length; i += 4) { const r = rgba[i] / 3 const g = rgba[i + 1] / 3 const b = rgba[i + 2] / 3 const pixelScore = r + g + b // 如果该像素足够明亮 if (pixelScore >= PIXEL_SCORE_THRESHOLD) { imageScore++ } } // 如果明亮的像素数量满足一定条件 if (imageScore >= IMAGE_SCORE_THRESHOLD) { // 产生了移动 }
在上述案例中,你也许会注意到画面是『绿色』的。其实,我们只需将每个像素的红和蓝设置为 0,即将 RGBA 的 r = 0; b = 0
即可。这样就会像电影的某些镜头一样,增加了科技感和神秘感。
体验地址>>
const rgba = imageData.data for (let i = 0; i < rgba.length; i += 4) { rgba[i] = 0 // red rgba[i + 2] = 0 // blue } ctx.putImageData(imageData, 0, 0)
将 RGBA 中的 R 和 B 置为 0
跟踪“移动物体”
有了明亮的像素后,我们就要找出其 x 坐标的最小值与 y 坐标的最小值,以表示跟踪矩形的左上角。同理,x 坐标的最大值与 y 坐标的最大值则表示跟踪矩形的右下角。至此,我们就能绘制出一个能包围所有明亮像素的矩形,从而实现跟踪移动物体的效果。
웹 기술이 모바일 모니터링을 구현하는 방법
体验链接>>
示例代码:
function processDiff (imageData) { const rgba = imageData.data let score = 0 let pixelScore = 0 let motionBox = 0 // 遍历整个 canvas 的像素,以找出明亮的点 for (let i = 0; i < rgba.length; i += 4) { pixelScore = (rgba[i] + rgba[i+1] + rgba[i+2]) / 3 // 若该像素足够明亮 if (pixelScore >= 80) { score++ coord = calcCoord(i) motionBox = calcMotionBox(montionBox, coord.x, coord.y) } } return { score, motionBox } } // 得到左上角和右下角两个坐标值 function calcMotionBox (curMotionBox, x, y) { const motionBox = curMotionBox || { x: { min: coord.x, max: x }, y: { min: coord.y, max: y } } motionBox.x.min = Math.min(motionBox.x.min, x) motionBox.x.max = Math.max(motionBox.x.max, x) motionBox.y.min = Math.min(motionBox.y.min, y) motionBox.y.max = Math.max(motionBox.y.max, y) return motionBox } // imageData.data 是一个含有每个像素点 rgba 信息的一维数组。 // 该函数是将上述一维数组的任意下标转为 (x,y) 二维坐标。 function calcCoord(i) { return { x: (i / 4) % diffWidth, y: Math.floor((i / 4) / diffWidth) } }
在得到跟踪矩形的左上角和右下角的坐标值后,通过 ctx.strokeRect(x, y, width, height)
API 绘制出矩形即可。
ctx.lineWidth = 6 ctx.strokeRect( diff.motionBox.x.min + 0.5, diff.motionBox.y.min + 0.5, diff.motionBox.x.max - diff.motionBox.x.min, diff.motionBox.y.max - diff.motionBox.y.min )
这是理想效果,实际效果请打开 体验链接
扩展:为什么上述绘制矩形的代码中的
x、y
要加0.5
呢?一图胜千言:
性能缩小尺寸
在上一个章节提到,我们需要通过对 Canvas 每个像素进行处理,假设 Canvas 的宽为 640
,高为 480
,那么就需要遍历 640 * 480 = 307200
个像素。而在监测效果可接受的前提下,我们可以将需要进行像素处理的 Canvas 缩小尺寸,如缩小 10 倍。这样需要遍历的像素数量就降低 100
倍,从而提升性能。
体验地址>>
示例代码:
const motionCanvas // 展示给用户看 const backgroundCanvas // offscreen canvas 背后处理数据 motionCanvas.width = 640 motionCanvas.height = 480 backgroundCanvas.width = 64 backgroundCanvas.height = 48
尺寸缩小 10 倍
定时器
我们都知道,当游戏以『每秒60帧』运行时才能保证一定的体验。但对于我们目前的案例来说,帧率并不是我们追求的第一位。因此,每 100 毫秒(具体数值取决于实际情况)取当前帧与前一帧进行比较即可。
另外,因为我们的动作一般具有连贯性,所以可取该连贯动作中幅度最大的(即“分数”最高)或最后一帧动作进行处理即可(如存储到本地或分享到朋友圈)。
延伸
至此,用 Web 技术实现简易的“移动监测”效果已基本讲述完毕。由于算法、设备等因素的限制,该效果只能以 2D 画面为基础来判断物体是否发生“移动”。而微软的 Xbox、索尼的 PS、任天堂的 Wii 等游戏设备上的体感游戏则依赖于硬件。以微软的 Kinect 为例,它为开发者提供了可跟踪最多六个完整骨骼和每人 25 个关节等强大功能。利用这些详细的人体参数,我们就能实现各种隔空的『手势操作』,如画圈圈诅咒某人。
下面几个是通过 Web 使用 Kinect 的库:
DepthJS:以浏览器插件形式提供数据访问。
Node-Kinect2: 以 Nodejs 搭建服务器端,提供数据比较完整,实例较多。
ZigFu: H5, U3D, Flash를 지원하며 비교적 완전한 API를 갖추고 있습니다.
Kinect-HTML5: Kinect-HTML5는 C#을 사용하여 서버를 구축하고 색상 데이터, 깊이 데이터 및 뼈 데이터를 제공합니다. Node-Kinect2 관련 권장 사항을 통한 골격 데이터 Acquire 골격 데이터 :
ajax 작동 글로벌 모니터링, 사용자 세션에 대한 솔루션 실패 110 시간 모니터링 카테고리의 컨텐츠 권장 사항
PHP 타이머 페이지 러닝타임 모니터링 수업
위 내용은 웹 기술이 모바일 모니터링을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!