使用 Webcam 实现画面监控告警

既然浏览器内可以调动摄像头,那能不能写一个页面监控画面变化,让家里的笔记本、旧手机或者树莓派设备变身监控摄像头呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<!DOCTYPE html>

<video width="640" height="480" autoplay></video>
<canvas width="640" height="480"></canvas>
<canvas width="640" height="480"></canvas>

<script>

var video = document.querySelector('video');
var canvas = document.querySelectorAll('canvas')[0];
var canvasForDiff = document.querySelectorAll('canvas')[1];

// video捕获摄像头画面
navigator.mediaDevices.getUserMedia({
video: true,
// audio: true,
}, success, error);

function success(stream) {
video.src = window.webkitURL.createObjectURL(stream);
video.play();
}

function error(err) {
alert('video error: ' + err)
}

//canvas
var context = canvas.getContext('2d'),
diffCtx = canvasForDiff.getContext('2d');
//将第二个画布混合模式设为“差异”
diffCtx.globalCompositeOperation = 'difference';

var preFrame, //前一帧
curFrame; //当前帧

var diffFrame; //存放差异帧的imageData

//捕获并保存帧内容
function captureAndSaveFrame(){
preFrame = curFrame;
context.drawImage(video, 0, 0, 640, 480);
curFrame = canvas.toDataURL(); //转为base64并保存
}

//绘制base64图像到画布上
function drawImg(src, ctx){
ctx = ctx || diffCtx;
var img = new Image();
img.src = src;
ctx.drawImage(img, 0, 0, 640, 480);
}

//渲染前后两帧差异
function renderDiff(){
if(!preFrame || !curFrame) return;
diffCtx.clearRect(0, 0, 640, 480);
drawImg(preFrame);
drawImg(curFrame);
diffFrame = diffCtx.getImageData( 0, 0, 640, 480 ); //捕获差异帧的imageData对象
}

//计算差异
function calcDiff(){
if(!diffFrame) return 0;
var cache = arguments.callee,
count = 0;
cache.total = cache.total || 0; //整个画布都是白色时所有像素的值的总和
for (var i = 0, l = diffFrame.width * diffFrame.height * 4; i < l; i += 4) {
count += diffFrame.data[i] + diffFrame.data[i + 1] + diffFrame.data[i + 2];
if(!cache.isLoopEver){ //只需在第一次循环里执行
cache.total += 255 * 3; //单个白色像素值
}
}
cache.isLoopEver = true;
count *= 3; //亮度放大
//返回“差异画布高亮部分像素总值”占“画布全亮情况像素总值”的比例
return Number(count/cache.total).toFixed(2);
}


//异常图片上传处理
function submit(){
var diff = '<img src="' + curFrame + '" />';
// TODO: 将画面 base64 上报到你的服务地址或发送邮件告警
}

//定时捕获
function timer(delta){
setTimeout(function(){
captureAndSaveFrame();
renderDiff();
if(calcDiff() > 0.2){ //监控到异常,发日志
submit()
}

timer(delta)
}, delta || 500);
}

timer();
</script>