File size: 4,009 Bytes
987f315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
135
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());