PoseNet

PoseNet

Pose-uppskattning avser tekniker som upptäcker mänskliga figurer i bilder och video, så att man till exempel kan avgöra var någons armbåge dyker upp i en bild. Den här tekniken känner inte igen vem som är på en bild – det finns ingen personlig identifierbar information kopplad till poseringsdetektering. Algoritmen uppskattar helt enkelt var viktiga kroppsleder finns.

Den modell som används här heter PoseNet och identifierar 17 punkter på en människas kropp.

Länk till ett program där modellen används för att ändra bild på en presentation genom att användaren lyfter höger arm upp mot toppen av videobilden för en bild framåt och vänster arm för en bild bakåt.

Image of landmarkt in PoseNet model

Man kan använda en bild eller ett videoflöde via webkameran för att identifiera punkterna. När modellen ser en bild så returnerar den x-koordinat, y-koordinat samt hur säker den är på sin uppskattning. Modellen vill ha en kvadratisk bild eller kvadratisk videoinput.

I det här programmet kommer vi att använda webkameran för att identifiera de olika punkterna som modellen identifierar. VI kommer att rita ut punkter för dem och streck (skeleton) mellan dem.

De bibliotek vi använder är ml5.js och p5.js.

Kod som står i de ljusgröna fälten nedan är den kod som ska skrivas i editorn. De grå fälten är där kod visas för en närmare beskrivning av delar av koden.

Starta en ny sketch via p5.js editor.

I index.html måste vi lägga till några bibliotek för att få tillgång till ml5.js.
Efter de script som finns lägg till:

<script src=”https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/addons/p5.dom.min.js”></script>
<script src=”https://unpkg.com/ml5@latest/dist/ml5.min.js” type=”text/javascript”></script>

script.js skapas programmet med JavaScript, p5.js.

Det finns några variabler som vi ska använda på lite olika platser i koden. För att kunna göra det behöver variablerna vara globala, dvs skapade utanför alla funktioner.
Vi skapar dem med let och ger dem inget initialt värde.

let video;
let poseNet;
let pose;
let skeleton;

video är den video vi vill visa på canvasen, poseNetär själva modellen. pose är ett objekt vilket innehåller information om modellens olika punkter och med vilken säkerhet modellen ror sig ha identifierat dessa. skeleton är en array med de olika punkternas position i x- och y-led.

I funktionen setup() skapas canvas vilken är 640 * 640 pixlar. PoseNet modellen föredrar en kvadratisk canvas och eller bild. Video skapas med p5.js funktion createCanvas(). För att slippa få ett videoelement under canvasen så används video.hide().

poseNet = ml5.poseNet(video, modelLoaded); poseNet sätts till modellen och i parentesen skriver vi att det är video som ska användas och att funktionen modelLoaded ska kallas på när modellen har laddats klart i programmet. Den funktionen måste vi skapa senare.

poseNet.on(’pose’, gotPoses); den här raden betyder att när modellen upptäckt en pose ska funktionen gotPoses kallas på. Även den funktionen måste skapas senare.

Hela koden för funktionen setup:

function setup() {
  createCanvas(640, 640);
  video = createCapture(VIDEO);
  video.hide();
  poseNet = ml5.poseNet(video, modelLoaded);
  poseNet.on('pose', gotPoses);
}

Efter funktionen setup, skapa funktionerna modelLoaded som skriver ut i konsolen att Model Loaded! och gotPoses vilken kontrollerar om det finns en pose med if-sats. Om det är så så sparas det första resultatet i arrayen poses[0].pose till variabeln pose vilken vi skapade globalt tidigare. Skeleton sparas på samma sätt till variabeln skeleton.

Om du behöver uppdatera dig på if-satser kan du titta här.

function modelLoaded(){
  console.log('Model Loaded');
}


function gotPoses(poses){
  if(poses.length > 0){
    pose = poses[0].pose;
    skeleton = poses[0].skeleton;
  }
}

I draw() ritas videon ut på canvasen och det kontrolleras om det finns en pose med en if-sats.
Om if-satsen är sann (true) skapas koden nedan i den.

Med en for-loop loopas alla keypoints och deras position i x- och y-led sparas i variabler x och y.
Det ritas ut en grön ellipse på dessa positioner.
Om du behöver uppdatera dig på for-loop kan du titta här.

  image(video, 0, 0, width, height);
  if(pose){
    for(let i = 0; i < pose.keypoints.length; i++){
      let x = pose.keypoints[i].position.x;
      let y = pose.keypoints[i].position.y;
      noStroke()
      fill(0, 255, 0);
      ellipse(x, y, 16, 16);
    }

En annan for-loop skapas för att loopa igenom alla skeleton delarna vilka sparas i variablerna a och b.
Skelettet ritas ut som en linje mellan a och b.

    for(let i = 0; i < skeleton.length; i++){
      let a = skeleton[i][0];
      let b = skeleton[i][1];
      stroke(255);
      strokeWeight(2);
      line(a.position.x, a.position.y, b.position.x, b.position.y)
    }

Hela koden för funktionen draw:

function draw() {
  image(video, 0, 0, width, height);
  if(pose){
    for(let i = 0; i < pose.keypoints.length; i++){
      let x = pose.keypoints[i].position.x;
      let y = pose.keypoints[i].position.y;
      noStroke()
      fill(0, 255, 0);
      ellipse(x, y, 16, 16);
    }
    for(let i = 0; i < skeleton.length; i++){
      let a = skeleton[i][0];
      let b = skeleton[i][1];
      stroke(255);
      strokeWeight(2);
      line(a.position.x, a.position.y, b.position.x, b.position.y)
    }
  }
}

Stå en bit ifrån webkameran så att hela kroppen syns på videon för bästa resultat.

Hela koden hittar du här.