File size: 5,226 Bytes
30c32c8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
(function () {
    const BENCHMARK_THROTTLE = 250;
    const INTERVAL = 33;

    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-disable-next-line no-console
        console.log(err);
    });

    const VideoMotion = window.Scratch3VideoSensingDebug.VideoMotion;
    const VideoMotionView = window.Scratch3VideoSensingDebug.VideoMotionView;

    // Create motion detector
    const motion = new VideoMotion();

    // Create debug views that will render different slices of how the detector
    // uses 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_CELL];

    // Add activation toggles for each debug view.
    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 textContainer = document.createElement('div');
    const textHeader = document.createElement('div');
    textHeader.innerText = 'duration (us) :: motion amount :: motion direction';
    textContainer.appendChild(textHeader);
    const textEl = document.createElement('div');
    textEl.innerText = `0 :: 0 :: 0`;
    textContainer.appendChild(textEl);
    document.body.appendChild(textContainer);
    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, INTERVAL);

        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,
                -tempCanvas.width, 0, tempCanvas.width, tempCanvas.height
            );
            ctx.resetTransform();
            const data = ctx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);

            // Analyze the latest frame.
            const b = performance.now();
            motion.addFrame(data.data);
            motion.analyzeFrame();

            // Every so often update the visible debug numbers with duration in
            // microseconds, the amount of motion and the direction of the
            // motion.
            if (Date.now() - textTimer > BENCHMARK_THROTTLE) {
                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-disable-next-line no-console
            console.error(error.stack || error);
            clearTimeout(timeoutId);
        }
    };

    loop();
}());