In deze les gaan we leren hoe je specifieke handposes, zoals bijvoorbeeld 👊 📃 ✂️ kan herkennen! Daarmee kunnen we "rock, paper, scissors" gaan spelen.
- Werken met KNN in javascript
- Data verzamelen uit MediaPipe en opslaan als JSON.
- Data uit MediaPipe voorspellen met KNN
Dit algoritme kan sets van getallen met elkaar vergelijken om te zien welke het meest overeenkomen. We gaan dit gebruiken om handposes te vergelijken.
Een handpose heeft 63 punten (21 keer x, y, en z), maar het werkt ook met kleinere getallenreeksen.
In deze afbeelding zie je hoe KNN werkt. Door de getallenreeks weight, ear length
als een x,y
grafiek te tekenen kan je goed zien dat katten en honden in een eigen groepje zitten qua afstand.
Als we een nieuw punt tekenen in de grafiek, kunnen we via de afstand tot de andere punten bepalen of het nieuwe punt een kat of een hond is! Zie ook dit interactief voorbeeld op Codepen.
Download het bestand knear.js en voeg het toe aan jouw project. Maak een app.js aan:
import kNear from "./knear.js"
const k = 3
const machine = new kNear(k);
Je gaat het KNN algoritme trainen met data. Je ziet dat data bestaat uit een array van getallen en een label.
machine.learn([6, 5, 9, 4], 'cat')
machine.learn([12, 20, 19, 3], 'dog')
Vul het voorbeeld aan met alle data uit onderstaande tabel.
Body length | Height | Weight | Ear length | Label |
---|---|---|---|---|
18 | 9.2 | 8.1 | 2 | 'cat' |
20.1 | 17 | 15.5 | 5 | 'dog' |
17 | 9.1 | 9 | 1.95 | 'cat' |
23.5 | 20 | 20 | 6.2 | 'dog' |
16 | 9.0 | 10 | 2.1 | 'cat' |
21 | 16.7 | 16 | 3.3 | 'dog' |
Als je met voldoende data getraind hebt, kan je een voorspelling doen. Dit betekent dat je van een onbekend dier gaat voorspellen of het een hond of een kat is.
let prediction = machine.classify([12,18,17,12])
console.log(`I think this is a ${prediction}`)
Als je de basis van KNN onder de knie hebt, dan kan je deze kennis gaan gebruiken om handposes te herkennen.
Dit werkt eigenlijk precies hetzelfde, alleen moet je nu met handpose data gaan werken. Die data hadden we in les 1 al in de console gelogd:
console.log(results.landmarks)
De uitdaging wordt nu om deze data om te zetten naar het formaat waar KNN mee kan werken.
Maak een button die console.log(results.landmarks[0])
uitvoert zodra je er op klikt.
Let op dat er twee handen kunnen zijn, dit zijn
results.landmarks[0]
enresults.landmarks[1]
.
Een enkele pose bestaat uit een array van 21 punten, een console.log
moet er als volgt uit gaan zien:
[
{x: 0.1, y: 0.3, z: 0.6},
{x: 0.2, y: 0.7, z: 0.9},
// ...in totaal 21 punten
]
De volgende stap is om dit console bericht te versimpelen. Kijk of je de x,y,z
waarden van alle 21 punten achter elkaan in de console kan tonen. Dit zijn dus 63 getallen..
[0.3.0.1.0.13.0.41.0.24.0.24,0.3...] // 63 getallen
Je kan de data omzetten met een
for
loop. Bekijk ook demap()
functie, en deflat()
functie.
Als het je gelukt is om een pose te tonen als simpele array, dan kan je een JSON file aanmaken waarin je jouw posedata gaat opslaan. De data van een pose kan je telkens handmatig copy>pasten uit de console naar je JSON file.
Je moet per array een label opslaan, anders weet je niet wat voor pose bij de data hoort. Doe dit voor drie poses, waarbij je per pose 5 voorbeelden verzamelt. Je JSON file kan er dan zo uit gaan zien:
[
{points:[0.3.0.1.0.13.0.41.0.24.0,...], label:"rock"},
{points:[0.3.0.1.0.13.0.41.0.24.0,...], label:"rock"},
{points:[0.3.0.1.0.13.0.41.0.24.0,...], label:"rock"},
// .. en nog meer poses voor paper en scissors
]
Bekijk dit voorbeeld bestand als je er niet uit komt.
Het omzetten van data was meer werk dan het tonen van poses uit de webcam! Dit is waarom het vak van AI ook wel Data Science heet...
Nu we posedata als json file hebben kunnen we dit leren aan KNN. Mocht je nog geen werkend JSON bestand hebben gebruik dan dit voorbeeld bestand.
json laden
fetch("mydata.json")
.then(response => response.json())
.then(data => train(data))
.catch(error => console.log(error))
data in knn
function train(poses) {
for(let pose of poses) {
// dubbel check of je hier een correct pose ziet:
// {points:[2,4,5,3,...]}, "rock"}
console.log(pose)
// geef de data aan de learn functie
// machine.learn([2,4,5,3,...], "rock")
machine.learn(pose.points, pose.label)
}
}
Je kan de JSON ook in localStorage
opslaan, dat maakt het makkelijker om snel in te laden en te testen. Let op dat het dan alleen op jouw eigen machine werkt.
Als het trainen gelukt is kunnen we eindelijk testen of het hele proces goed is gegaan.
Je kan met classify
een van je bestaande poses invoeren, zonder het label om te kijken of KNN ziet welke pose dit is:
function classifyPose(newpose) {
let result = machine.classify(newpose)
}
classifyPose([2,3,4,5,6,7,...]) // een array uit je json file, zonder label
Als al het bovenstaande werkt kan je de console.log()
code van je button aanpassen. De button gaat de array van getallen nu ook naar de classifyPose()
functie sturen.
Je kan deze afbeelding als referentie gebruiken voor gebarentaal.
- De labels kloppen niet of je bent labels vergeten.
- Niet elke handpose heeft 63 getallen, of je hebt getallen opgeslagen als strings. (bv.
pose="5,2,5,2"
) - De data in je
machine.learn()
aanroep moet een array zijn, gevolgd door een label. Dit mag dus geen object zijn. - De array in je
machine.classify()
aanroep moet óók precies 63 getallen bevatten.
// fout
machine.learn({pose:[2,4,5,3,...], label:"rock"})
let result = machine.classify({pose:[2,4,5,3,...]})
// goed
machine.learn([2,4,5,3,...], "rock")
let result = machine.classify([2,4,5,3,...])