Bonjour,

Je suis en train de m'amuser un peu à coder :
  • afficher le flux vidéo d'une webcam dans Firefox
  • faire une détection de mouvement
  • afficher le nom de l'objet qui a déclenché le mouvement


Je n'ai rien inventé mais juste mis en oeuvre des choses existantes :
  • tensorflow pour la reconnaissance d'objets
  • diff-cam-engine.js pour la détection de mouvement


Le code n'est pas terrible (junkCode).

index.html
Code html : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link href="site.css" rel="stylesheet">
	</head>
	<body>
		<script src="diff-cam-engine.js"></script>
		<script src="site.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
		<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd"></script>
		<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0"> </script>
		<script>main();</script>
	</body>
</html>

site.css
Code CSS : Sélectionner tout - Visualiser dans une fenêtre à part
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
body {
	margin: 170px 20px 20px;
	text-align: center;
}
 
.wrapper {
	display: inline-block;
	position: relative;
	width: 640px;
	height: 480px;
	background-color: #000;
}
 
img {
	position: absolute;
	right: 10px;
	top: -160px;
}
 
.knocked-over {
	transform: rotate(450deg);
	transition: transform 1s;
}
 
video {
	position: absolute;
	left: 0;
	top: 0;
	width: 640px;
	height: 480px;
	transform: scaleX(-1); /* flip video, like a mirror */
}
 
.motion-box {
	//display: none;
	position: absolute;
	border: 4px solid #fff;
	transition: all .1s;
}

site.js
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
let _model=undefined;
let _debug=1;
let _motionBox=document.querySelector("#motionBox");
 
let scale = 10;	// capture resolution over motion resolution
let isActivated = false;
let isTargetInSight = false;
let isKnockedOver = false;
let lostTimeout;
 
class $$page
{	constructor()
	{	this.frag=document.createDocumentFragment();
	 	this.parent=undefined;
	}
	add({tag="",id="",text="",border="",width="",height="",classes=""})
	{	let elem=document.createElement(tag);
		if(id!="") elem.id=id;
		if(text!="") elem.textContent=text;
		if(border!="") elem.style.border="10px solid red";
		if(width!="") elem.width=width;
 		if(height!="") elem.height=height;
	 	if(classes) elem.className=classes;
	 	if(this.parent==undefined)
		{	this.frag.appendChild(elem);
		 	this.parent=elem;
		}
	 	else
		{	this.parent.appendChild(elem);
		}
	}
	render()
	{	document.body.appendChild(this.frag);
	}
}
 
function initSuccess()
{	DiffCamEngine.start();
}
 
function initError()
{	alert('Something went wrong.');
}
 
function startComplete()
{	setTimeout(activate, 500);
}
 
function activate()
{	isActivated = true;
	debug("activated");
}
 
function capture(payload)
{	if (!isActivated || isKnockedOver)
	{	return;
	}
	let box=payload.motionBox;
	if (box)
	{	// video is flipped, so we're positioning from right instead of left
		let right=box.x.min*scale+1;
		let top=box.y.min*scale+1;
		let width=(box.x.max-box.x.min)*scale;
		let height=(box.y.max-box.y.min)*scale;
		let videoElem=document.querySelector("#video");
		let motionElem=document.querySelector("#motionBox");
		motionElem.style.position="absolute";
		motionElem.style.right=right+"px";
		motionElem.style.top=top+"px";
		motionElem.style.width=width+"px";
		motionElem.style.height=height+"px";
		motionElem.style.border="2px solid red";
		motionElem.style.visibility="visible";
/*		Modèle mobilenet
		_model.classify(payload.imageData).then(predictions =>
		{	for(let i=0;i<predictions.length;i++)
			{	debug("Predictions: ",predictions[i].className+"=>"+predictions[i].probability);
			}
		});
*/
		// Modèle coco-ssd
		_model.detect(payload.imageData).then(predictions =>
		{	for(let i=0;i<predictions.length;i++)
			{	if(predictions[i].score>.85)
				{	debug("Predictions: ",predictions[i].class+"=>"+predictions[i].score);
				}
			}
		});
		if (!isTargetInSight)
		{	isTargetInSight=true;
			debug("seen");
		}
		else
		{	//debug("fire");
		}
 
		clearTimeout(lostTimeout);
		lostTimeout = setTimeout(declareLost, 2000);
	}
/*
	// video is flipped, so (0, 0) is at top right
	if (payload.checkMotionPixel(0, 0))
	{	knockOver();
	}
*/
}
 
function declareLost()
{	isTargetInSight = false;
 	document.querySelector("#motionBox").style.visibility="hidden";
 	debug("target lost");
}
 
function knockOver()
{	isKnockedOver = true;
	clearTimeout(lostTimeout);
	document.querySelector("#motionBox").style.visibility="hidden";
	debug("knock over");
}
 
function debug(text,...data)
{	if(_debug==1)
	{	if(data.length!=0)
		{	console.debug(new Date().toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit',fractionalSecondDigits:3})+" -> "+text,data);
		}
		else
		{	console.debug(new Date().toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit',fractionalSecondDigits:3})+" -> "+text);
		}
	}
}
 
async function modelLoad()
{	//_model=await mobilenet.load();
	_model=await cocoSsd.load();
}
 
async function main()
{	let $page=new $$page();
	$page.add({tag:"div",classes:"wrapper"});
	$page.add({tag:"video",id:"video",classes:"wrapper"});
	$page.add({tag:"div",id:"motionBox",classes:"motion-box"});
	$page.render();
	document.querySelector('#video').autoplay=true;
	DiffCamEngine.init({video: document.getElementById('video'),captureIntervalTime: 50,includeMotionBox: true,includeMotionPixels: true,initSuccessCallback: initSuccess,initErrorCallback: initError,startCompleteCallback: startComplete,captureCallback: capture});
	//coSsd.load().then(model =>{_model=model;});
	await modelLoad();
}
NB : activer la console debug (F12) pour voir les évènements et données
Si cela peut intéresser qqn...