mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-08-01 17:11:21 -04:00
Add motion playground page for debugging video sensing
This commit is contained in:
parent
3c473f6ba8
commit
c206dc3241
3 changed files with 142 additions and 1 deletions
18
src/playground/motion.html
Normal file
18
src/playground/motion.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video Motion Test Playground</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- FPS counter, Syntax highlighter, Blocks, Renderer -->
|
||||
<script src="./vendor.js"></script>
|
||||
<!-- Storage module -->
|
||||
<script src="./scratch-storage.js"></script>
|
||||
<!-- Stage rendering -->
|
||||
<script src="./scratch-render.js"></script>
|
||||
<!-- Extension -->
|
||||
<script src="./motion-extension.js"></script>
|
||||
<!-- Motion -->
|
||||
<script src="./motion.js"></script>
|
||||
</body>
|
||||
</html>
|
118
src/playground/motion.js
Normal file
118
src/playground/motion.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
(function () {
|
||||
const video = document.createElement('video');
|
||||
navigator.getUserMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
width: {min: 480, ideal: 640},
|
||||
height: {min: 360, ideal: 480}
|
||||
}
|
||||
}, stream => {
|
||||
video.autoplay = true;
|
||||
video.src = window.URL.createObjectURL(stream);
|
||||
// Get the track to hint to the browser the stream needs to be running
|
||||
// even though we don't add the video tag to the DOM.
|
||||
stream.getTracks();
|
||||
video.addEventListener('play', () => {
|
||||
video.width = video.videoWidth;
|
||||
video.height = video.videoHeight;
|
||||
});
|
||||
}, err => {
|
||||
/* eslint no-console:0 */
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
const VideoMotion = window.Scratch3MotionDetect.VideoMotion;
|
||||
const VideoMotionView = window.Scratch3MotionDetect.VideoMotionView;
|
||||
|
||||
// Create motion detector
|
||||
const motion = new VideoMotion();
|
||||
|
||||
// Create debug views that will render different slices of how the detector
|
||||
// uses the a frame of input.
|
||||
const OUTPUT = VideoMotionView.OUTPUT;
|
||||
const outputKeys = Object.keys(OUTPUT);
|
||||
const outputValues = Object.values(OUTPUT);
|
||||
const views = outputValues
|
||||
.map(output => new VideoMotionView(motion, output));
|
||||
const view = views[0];
|
||||
|
||||
const defaultViews = [OUTPUT.INPUT, OUTPUT.XY_CELL, OUTPUT.T_CELL, OUTPUT.UV];
|
||||
|
||||
const activators = document.createElement('div');
|
||||
activators.style.userSelect = 'none';
|
||||
outputValues.forEach((output, index) => {
|
||||
const checkboxLabel = document.createElement('label');
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.checked = defaultViews.indexOf(output) !== -1;
|
||||
const checkboxSpan = document.createElement('span');
|
||||
checkboxSpan.innerText = outputKeys[index];
|
||||
checkboxLabel.appendChild(checkbox);
|
||||
checkboxLabel.appendChild(checkboxSpan);
|
||||
|
||||
const _view = views[index];
|
||||
_view.canvas.style.display = checkbox.checked ? '' : 'none';
|
||||
_view.active = checkbox.checked;
|
||||
checkbox.onchange = event => {
|
||||
_view.canvas.style.display = checkbox.checked ? '' : 'none';
|
||||
_view.active = checkbox.checked;
|
||||
event.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
activators.appendChild(checkboxLabel);
|
||||
});
|
||||
document.body.appendChild(activators);
|
||||
|
||||
// Add a text line to display milliseconds per frame, motion value, and
|
||||
// motion direction
|
||||
const textEl = document.createElement('div');
|
||||
document.body.appendChild(textEl);
|
||||
let textTimer = Date.now();
|
||||
|
||||
// Add the motion debug views to the dom after the text line, so the text
|
||||
// appears first.
|
||||
views.forEach(_view => document.body.appendChild(_view.canvas));
|
||||
|
||||
// Create a temporary canvas the video will be drawn to so the video's
|
||||
// bitmap data can be transformed into a TypeArray.
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = view.canvas.width;
|
||||
tempCanvas.height = view.canvas.height;
|
||||
const ctx = tempCanvas.getContext('2d');
|
||||
|
||||
const loop = function () {
|
||||
const timeoutId = setTimeout(loop, 33);
|
||||
|
||||
try {
|
||||
// Get the bitmap data for the video frame
|
||||
ctx.scale(-1, 1);
|
||||
ctx.drawImage(
|
||||
video,
|
||||
0, 0, video.width || video.clientWidth, video.height || video.clientHeight,
|
||||
-480, 0, tempCanvas.width, tempCanvas.height
|
||||
);
|
||||
ctx.resetTransform();
|
||||
const data = ctx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
|
||||
|
||||
const b = performance.now();
|
||||
motion.addFrame(data.data);
|
||||
motion.analyzeFrame();
|
||||
if (Date.now() - textTimer > 250) {
|
||||
const e = performance.now();
|
||||
const analyzeDuration = ((e - b) * 1000).toFixed(0);
|
||||
const motionAmount = motion.motionAmount.toFixed(1);
|
||||
const motionDirection = motion.motionDirection.toFixed(1);
|
||||
textEl.innerText = `${analyzeDuration} :: ${motionAmount} :: ${motionDirection}`;
|
||||
textTimer = Date.now();
|
||||
}
|
||||
views.forEach(_view => _view.active && _view.draw());
|
||||
} catch (error) {
|
||||
/* eslint no-console:0 */
|
||||
console.error(error.stack || error);
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
|
||||
loop();
|
||||
}());
|
|
@ -86,7 +86,8 @@ module.exports = [
|
|||
'scratch-storage',
|
||||
// Renderer
|
||||
'scratch-render'
|
||||
]
|
||||
],
|
||||
'motion-extension': './src/extensions/scratch3_video_sensing/debug'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'playground'),
|
||||
|
@ -98,6 +99,10 @@ module.exports = [
|
|||
test: require.resolve('./src/index.js'),
|
||||
loader: 'expose-loader?VirtualMachine'
|
||||
},
|
||||
{
|
||||
test: require.resolve('./src/extensions/scratch3_video_sensing/debug.js'),
|
||||
loader: 'expose-loader?Scratch3MotionDetect'
|
||||
},
|
||||
{
|
||||
test: require.resolve('stats.js/build/stats.min.js'),
|
||||
loader: 'script-loader'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue