Skip to content

Commit bf5d194

Browse files
authored
The original implementation - slower.
Keeping this here for reference. This was the original implementation - which was slightly more granular in patch updates, but 10x slower.
1 parent 02bb8ce commit bf5d194

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

script_original.js

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
2+
/**
3+
* @license
4+
* Copyright 2018 Google LLC. All Rights Reserved.
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
* =============================================================================
17+
*/
18+
19+
/********************************************************************
20+
* Real-Time-Person-Removal Created by Jason Mayes 2020.
21+
*
22+
* Get latest code on my Github:
23+
* https://github.com/jasonmayes/Real-Time-Person-Removal
24+
*
25+
* Got questions? Reach out to me on social:
26+
* Twitter: @jason_mayes
27+
* LinkedIn: https://www.linkedin.com/in/creativetech
28+
********************************************************************/
29+
30+
const video = document.getElementById('webcam');
31+
const liveView = document.getElementById('liveView');
32+
const demosSection = document.getElementById('demos');
33+
const DEBUG = false;
34+
35+
36+
// An object to configure parameters to set for the bodypix model.
37+
// See github docs for explanations.
38+
const bodyPixProperties = {
39+
architecture: 'MobileNetV1',
40+
outputStride: 16,
41+
multiplier: 0.75,
42+
quantBytes: 4
43+
};
44+
45+
// An object to configure parameters for detection. I have raised
46+
// the segmentation threshold to 90% confidence to reduce the
47+
// number of false positives.
48+
const segmentationProperties = {
49+
flipHorizontal: false,
50+
internalResolution: 'high',
51+
segmentationThreshold: 0.9
52+
};
53+
54+
55+
// Must be even. The size of square we wish to search for body parts.
56+
// This is the smallest area that will render/not render depending on
57+
// if a body part is found in that square.
58+
const SEARCH_RADIUS = 300;
59+
const SEARCH_OFFSET = SEARCH_RADIUS / 2;
60+
61+
// RESOLUTION_MIN should be smaller than SEARCH RADIUS. About 10x smaller seems to
62+
// work well. Effects overlap in search space to clean up body overspill for things
63+
// that were not classified as body but infact were.
64+
const RESOLUTION_MIN = 20;
65+
66+
// Render returned segmentation data to a given canvas context.
67+
function processSegmentation(canvas, segmentation) {
68+
var ctx = canvas.getContext('2d');
69+
70+
// Get data from our overlay canvas which is attempting to estimate background.
71+
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
72+
var data = imageData.data;
73+
74+
// Get data from the live webcam view which has all data.
75+
var liveData = videoRenderCanvasCtx.getImageData(0, 0, canvas.width, canvas.height);
76+
var dataL = liveData.data;
77+
78+
// Now loop through and see if pixels contain human parts. If not, update
79+
// backgound understanding with new data.
80+
for (let x = RESOLUTION_MIN; x < canvas.width; x += RESOLUTION_MIN) {
81+
for (let y = RESOLUTION_MIN; y < canvas.height; y += RESOLUTION_MIN) {
82+
// Convert xy co-ords to array offset.
83+
let n = y * canvas.width + x;
84+
85+
let foundBodyPartNearby = false;
86+
87+
// Let's check around a given pixel if any other pixels were body like.
88+
let yMin = y - SEARCH_OFFSET;
89+
yMin = yMin < 0 ? 0: yMin;
90+
91+
let yMax = y + SEARCH_OFFSET;
92+
yMax = yMax > canvas.height ? canvas.height : yMax;
93+
94+
let xMin = x - SEARCH_OFFSET;
95+
xMin = xMin < 0 ? 0: xMin;
96+
97+
let xMax = x + SEARCH_OFFSET;
98+
xMax = xMax > canvas.width ? canvas.width : xMax;
99+
100+
for (let i = xMin; i < xMax; i++) {
101+
for (let j = yMin; j < yMax; j++) {
102+
103+
let offset = j * canvas.width + i;
104+
// If any of the pixels in the square we are analysing has a body
105+
// part, mark as contaminated.
106+
if (segmentation.data[offset] !== 0) {
107+
foundBodyPartNearby = true;
108+
break;
109+
}
110+
}
111+
}
112+
113+
// Update patch if patch was clean.
114+
if (!foundBodyPartNearby) {
115+
for (let i = xMin; i < xMax; i++) {
116+
for (let j = yMin; j < yMax; j++) {
117+
// Convert xy co-ords to array offset.
118+
let offset = j * canvas.width + i;
119+
120+
data[offset * 4] = dataL[offset * 4];
121+
data[offset * 4 + 1] = dataL[offset * 4 + 1];
122+
data[offset * 4 + 2] = dataL[offset * 4 + 2];
123+
data[offset * 4 + 3] = 255;
124+
}
125+
}
126+
} else {
127+
if (DEBUG) {
128+
for (let i = xMin; i < xMax; i++) {
129+
for (let j = yMin; j < yMax; j++) {
130+
// Convert xy co-ords to array offset.
131+
let offset = j * canvas.width + i;
132+
133+
data[offset * 4] = 255;
134+
data[offset * 4 + 1] = 0;
135+
data[offset * 4 + 2] = 0;
136+
data[offset * 4 + 3] = 255;
137+
}
138+
}
139+
}
140+
}
141+
142+
}
143+
}
144+
ctx.putImageData(imageData, 0, 0);
145+
}
146+
147+
148+
149+
// Let's load the model with our parameters defined above.
150+
// Before we can use bodypix class we must wait for it to finish
151+
// loading. Machine Learning models can be large and take a moment to
152+
// get everything needed to run.
153+
var modelHasLoaded = false;
154+
var model = undefined;
155+
156+
model = bodyPix.load(bodyPixProperties).then(function (loadedModel) {
157+
model = loadedModel;
158+
modelHasLoaded = true;
159+
// Show demo section now model is ready to use.
160+
demosSection.classList.remove('invisible');
161+
});
162+
163+
164+
/********************************************************************
165+
// Continuously grab image from webcam stream and classify it.
166+
********************************************************************/
167+
168+
var previousSegmentationComplete = true;
169+
170+
// Check if webcam access is supported.
171+
function hasGetUserMedia() {
172+
return !!(navigator.mediaDevices &&
173+
navigator.mediaDevices.getUserMedia);
174+
}
175+
176+
177+
// This function will repeatidly call itself when the browser is ready to process
178+
// the next frame from webcam.
179+
function predictWebcam() {
180+
if (previousSegmentationComplete) {
181+
// Copy the video frame from webcam to a tempory canvas in memory only (not in the DOM).
182+
videoRenderCanvasCtx.drawImage(video, 0, 0);
183+
previousSegmentationComplete = false;
184+
// Now classify the canvas image we have available.
185+
model.segmentPerson(videoRenderCanvas, segmentationProperties).then(function(segmentation) {
186+
processSegmentation(webcamCanvas, segmentation);
187+
previousSegmentationComplete = true;
188+
});
189+
}
190+
191+
// Call this function again to keep predicting when the browser is ready.
192+
window.requestAnimationFrame(predictWebcam);
193+
}
194+
195+
196+
// Enable the live webcam view and start classification.
197+
function enableCam(event) {
198+
if (!modelHasLoaded) {
199+
return;
200+
}
201+
202+
// Hide the button.
203+
event.target.classList.add('removed');
204+
205+
// getUsermedia parameters.
206+
const constraints = {
207+
video: true
208+
};
209+
210+
// Activate the webcam stream.
211+
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
212+
video.addEventListener('loadedmetadata', function() {
213+
// Update widths and heights once video is successfully played otherwise
214+
// it will have width and height of zero initially causing classification
215+
// to fail.
216+
webcamCanvas.width = video.videoWidth;
217+
webcamCanvas.height = video.videoHeight;
218+
videoRenderCanvas.width = video.videoWidth;
219+
videoRenderCanvas.height = video.videoHeight;
220+
let webcamCanvasCtx = webcamCanvas.getContext('2d');
221+
webcamCanvasCtx.drawImage(video, 0, 0);
222+
});
223+
224+
video.srcObject = stream;
225+
226+
video.addEventListener('loadeddata', predictWebcam);
227+
});
228+
}
229+
230+
231+
// We will create a tempory canvas to render to store frames from
232+
// the web cam stream for classification.
233+
var videoRenderCanvas = document.createElement('canvas');
234+
var videoRenderCanvasCtx = videoRenderCanvas.getContext('2d');
235+
236+
// Lets create a canvas to render our findings to the DOM.
237+
var webcamCanvas = document.createElement('canvas');
238+
webcamCanvas.setAttribute('class', 'overlay');
239+
liveView.appendChild(webcamCanvas);
240+
241+
// If webcam supported, add event listener to button for when user
242+
// wants to activate it.
243+
if (hasGetUserMedia()) {
244+
const enableWebcamButton = document.getElementById('webcamButton');
245+
enableWebcamButton.addEventListener('click', enableCam);
246+
} else {
247+
console.warn('getUserMedia() is not supported by your browser');
248+
}

0 commit comments

Comments
 (0)