Spaces:
Running
Running
class Posenet2Scratch { | |
constructor(runtime) { | |
this.runtime = runtime; | |
this.poses = []; | |
this.keypoints = []; | |
this._locale = this.setLocale(); | |
const video = document.createElement("video"); | |
video.width = 480; | |
video.height = 360; | |
video.autoplay = true; | |
video.style.display = "none"; | |
navigator.mediaDevices | |
.getUserMedia({ video: true, audio: false }) | |
.then((stream) => { | |
video.srcObject = stream; | |
}); | |
video.addEventListener("loadeddata", () => { | |
// Load posenet model and process poses | |
posenet.load().then((net) => { | |
const detectPose = () => { | |
net.estimateMultiplePoses(video, { | |
flipHorizontal: false, | |
}).then((poses) => { | |
this.poses = poses; | |
this.keypoints = poses[0]?.keypoints || []; | |
requestAnimationFrame(detectPose); | |
}); | |
}; | |
detectPose(); | |
}); | |
}); | |
this.runtime.ioDevices.video.enableVideo(); | |
} | |
setLocale() { | |
const supportedLocales = ["en", "ja", "ja-Hira"]; | |
const locale = navigator.language || "en"; | |
return supportedLocales.includes(locale) ? locale : "en"; | |
} | |
getX(args) { | |
const personIndex = parseInt(args.PERSON_NUMBER, 10) - 1; | |
const partIndex = parseInt(args.PART, 10); | |
const pose = this.poses[personIndex]; | |
if (pose && pose.pose.keypoints[partIndex]) { | |
const x = pose.pose.keypoints[partIndex].position.x; | |
return this.runtime.ioDevices.video.mirror ? 240 - x : x - 240; | |
} | |
return ""; | |
} | |
getY(args) { | |
const personIndex = parseInt(args.PERSON_NUMBER, 10) - 1; | |
const partIndex = parseInt(args.PART, 10); | |
const pose = this.poses[personIndex]; | |
if (pose && pose.pose.keypoints[partIndex]) { | |
const y = pose.pose.keypoints[partIndex].position.y; | |
return 180 - y; | |
} | |
return ""; | |
} | |
getPeopleCount() { | |
return this.poses.length; | |
} | |
getInfo() { | |
return { | |
id: "posenet2scratch", | |
name: "Posenet2Scratch", | |
blocks: [ | |
{ | |
opcode: "getX", | |
blockType: "reporter", | |
text: "[PART] x of person no. [PERSON_NUMBER]", | |
arguments: { | |
PERSON_NUMBER: { type: "string", menu: "personNumbers", defaultValue: "1" }, | |
PART: { type: "string", menu: "parts", defaultValue: "0" }, | |
}, | |
}, | |
{ | |
opcode: "getY", | |
blockType: "reporter", | |
text: "[PART] y of person no. [PERSON_NUMBER]", | |
arguments: { | |
PERSON_NUMBER: { type: "string", menu: "personNumbers", defaultValue: "1" }, | |
PART: { type: "string", menu: "parts", defaultValue: "0" }, | |
}, | |
}, | |
{ | |
opcode: "getPeopleCount", | |
blockType: "reporter", | |
text: "people count", | |
}, | |
], | |
menus: { | |
personNumbers: { | |
acceptReporters: true, | |
items: Array.from({ length: 10 }, (_, i) => ({ text: `${i + 1}`, value: `${i + 1}` })), | |
}, | |
parts: { | |
acceptReporters: true, | |
items: [ | |
{ text: "nose", value: "0" }, | |
{ text: "left eye", value: "1" }, | |
{ text: "right eye", value: "2" }, | |
{ text: "left ear", value: "3" }, | |
{ text: "right ear", value: "4" }, | |
{ text: "left shoulder", value: "5" }, | |
{ text: "right shoulder", value: "6" }, | |
{ text: "left elbow", value: "7" }, | |
{ text: "right elbow", value: "8" }, | |
{ text: "left wrist", value: "9" }, | |
{ text: "right wrist", value: "10" }, | |
{ text: "left hip", value: "11" }, | |
{ text: "right hip", value: "12" }, | |
{ text: "left knee", value: "13" }, | |
{ text: "right knee", value: "14" }, | |
{ text: "left ankle", value: "15" }, | |
{ text: "right ankle", value: "16" }, | |
], | |
}, | |
}, | |
}; | |
} | |
} | |
Scratch.extensions.register(new Posenet2Scratch()); | |