Skip to content

Instantly share code, notes, and snippets.

@schmidt-sebastian
Created June 16, 2023 15:37
Show Gist options
  • Save schmidt-sebastian/bb9b06988c8d4e09370770fbe3e9478f to your computer and use it in GitHub Desktop.
Save schmidt-sebastian/bb9b06988c8d4e09370770fbe3e9478f to your computer and use it in GitHub Desktop.
let video: HTMLVideoElement;
// Interval in seconds at which we refresh the results.
const DRAW_OBJECTS_INTERVAL = 0.1;
const DRAW_PROCESSING_TIME_INTERVAL = 0.25;
const messageTag = document.getElementById('message') as HTMLElement;
const outputBox = document.getElementById('container') as HTMLElement;
outputBox.style.bottom = '-30px';
const canvas = document.getElementById('output') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;
function drawBox(
label: string, x: number, y: number, w: number, h: number): void {
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.font = '24px roboto';
ctx.beginPath();
ctx.strokeStyle = 'white';
ctx.strokeText(label, x, y - 8);
ctx.strokeStyle = 'red';
ctx.moveTo(x, y);
ctx.lineTo(x + w, y);
ctx.lineTo(x + w, y + h);
ctx.lineTo(x, y + h);
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
}
// The last time in ms we processed data. Since we do not special handle the
// first detection, we only display accurate value after the second frame.
let lastVideoTime = -1;
let lastObjectDrawTime = -1;
let lastProcessingTimeDrawTime = -1;
let detectionsSinceLastRefresh = 0;
let worker: Worker;
// Process video detections in a loop
function detectLoop(): void {
if (video.currentTime !== lastVideoTime) {
const canvas = new OffscreenCanvas(video.videoWidth, video.videoHeight);
const context = canvas.getContext("2d");
context.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
worker.postMessage(imageData);
}
setTimeout(() => {
requestAnimationFrame(() => {
detectLoop();
});
}, 1000);
}
function drawObjects(detections: any): void {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const detection of detections) {
const box = detection.boundingBox!;
for (const category of detection.categories) {
const label = `${category.categoryName} (${category.index}): ${
category.score.toFixed(2)}`;
drawBox(label, box.originX, box.originY, box.width, box.height);
}
}
}
function drawProcessingTime(frameAverage: number): void {
messageTag.textContent = `Throughput: ${frameAverage.toFixed(2)} ms`;
}
// Stream webcam into detections loop (and also make video visible)
async function streamWebcamThroughDetector(): Promise<void> {
video = document.getElementById('video') as HTMLVideoElement;
function onAcquiredUserMedia(stream: MediaStream): void {
video.srcObject = stream;
video.onloadedmetadata = () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
video.play();
detectLoop();
};
}
try {
const evt = await navigator.mediaDevices.getUserMedia({
'audio': false,
'video': {
facingMode: 'user',
width: 1280,
height: 720,
}
});
onAcquiredUserMedia(evt);
} catch (e) {
console.error(`Failed to acquire camera feed: ${e}`);
}
}
async function runDemo() {
worker = new Worker("worker.js");
worker.onmessage = (e) => {
drawObjects(e.data.detections);
};
await streamWebcamThroughDetector();
}
runDemo();
import {ObjectDetector} from '@mediapipe/tasks-vision';
let detector: ObjectDetector;
ObjectDetector.createFromOptions(
{
wasmLoaderPath: `vision_wasm_internal.js`,
wasmBinaryPath: `vision_wasm_internal.wasm`,
},
{
baseOptions: {
modelAssetPath:
`coco_ssd_mobilenet_v1_1.0_quant_2018_06_29.tflite`
},
maxResults: 5,
}).then(d => {
detector = d;
});
onmessage = (e) => {
if (detector) {
const result = detector.detect(e.data);
(postMessage as any)(result );
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment