added rock paper scissors detector

This commit is contained in:
PotatoGamo
2025-10-06 20:21:05 -07:00
parent 70006a6ebd
commit 1c92aa8e7b
2 changed files with 300 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>handtracking</title>
<style>
body {
background-color: #1d1d1d;
width: 100%;
height: 100%;
overflow: scroll;
margin: 0;
padding: 0;
}
canvas {
padding: 0;
margin: 0;
border-color: #f00;
border-width: 1px;
}
</style>
<script src="../libraries/p5.min.js"></script>
<script src="../libraries/ml5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lil-gui@0.20"></script>
</head>
<body>
<h2 id="debug"></h2>
<script src="sketch.js"></script>
</body>
</html>
+266
View File
@@ -0,0 +1,266 @@
let video;
let handPose;
let hands = [];
let hand;
let handVals;
let fingers;
let handBounds;
let folders = {};
let painting;
let connections = [
[0, 1],
[1, 2],
[2, 3],
[3, 4],
[1, 5],
[5, 6],
[6, 7],
[7, 8],
[13, 9],
[9, 5],
[2, 5],
[9, 10],
[10, 11],
[11, 12],
[17, 13],
[13, 14],
[14, 15],
[15, 16],
[0, 17],
[17, 18],
[18, 19],
[19, 20]
];
let clearStart = 0;
let videoScale;
let conf = {
hands: {
palm_kps: [0, 1, 5, 9, 13, 17],
palm_kps_str: null,
Left: "#ff0000",
Right: "#0000ff",
show: true,
paint: true,
},
canvas: {
bg: "#00000000"
},
kps: {
size: 8,
color: "#000000",
draw: false,
},
kp_conn: {
connect: true,
stroke: 1,
},
kp_text: {
show: false,
size: 8,
offx: 10,
offy: 5,
color: "#000000ff"
},
camera: {
show: true,
},
hand_bounds: {
show: true,
stroke: 2
}
}
let palm_kps = [0, 1, 5, 17];
function preload() {
handPose = ml5.handPose({ flipped: true });
}
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
video = createCapture({
video: {
},
audio: false,
flipped: true,
});
videoScale = Math.min(window.innerWidth / 640, window.innerHeight / 480);
video.size(640 * videoScale, 480 * videoScale);
handPose.detectStart(video, (results) => { hands = results; });
video.hide();
}
function draw() {
clear();
strokeWeight(1);
stroke(255);
fill(conf.canvas.bg);
rect(0, 0, width, height);
rect(0, 0, video.width, video.height);
image(video, 0, 0);
handBounds = {};
if (hands.length > 0) {
hand = hands[0];
let index = {
x: hand.index_finger_tip.x,
y: hand.index_finger_tip.y
}
let middle = {
x: hand.middle_finger_tip.x,
y: hand.middle_finger_tip.y
}
let ring = {
x: hand.ring_finger_tip.x,
y: hand.ring_finger_tip.y
}
let pinky = {
x: hand.pinky_finger_tip.x,
y: hand.pinky_finger_tip.y
}
let thumb = {
x: hand.thumb_tip.x,
y: hand.thumb_tip.y
}
fingers = [thumb, index, middle, ring, pinky];
handVals = {
fingers: fingers,
handBounds: getBounds(hand.keypoints),
handRad: null,
palmBounds: getBounds(palm_kps.map(i => hand.keypoints[i])),
palmRad: null,
fingersBounds: getBounds(fingers),
fingersRad: null,
fingersInPalm: null,
fingersOnBounds: 0,
handedness: hand.handedness,
pose: "none",
}
handVals.palmRad = dist(handVals.palmBounds.min.x, handVals.palmBounds.min.y, handVals.palmBounds.max.x, handVals.palmBounds.max.y) / 2;
handVals.handRad = dist(handVals.handBounds.min.x, handVals.handBounds.min.y, handVals.handBounds.max.x, handVals.handBounds.max.y) / 2;
handVals.fingersRad = dist(handVals.fingersBounds.min.x, handVals.fingersBounds.min.y, handVals.fingersBounds.max.x, handVals.fingersBounds.max.y) / 2;
handVals.fingersInPalm = fingers.filter(f => dist(f.x, f.y, handVals.palmBounds.c.x, handVals.palmBounds.c.y) < handVals.palmRad * 1.2).length;
handVals.fingersOnBounds = fingers.filter(f => dist(f.x, f.y, handVals.fingersBounds.c.x, handVals.fingersBounds.c.y) < handVals.fingersRad * 1.2).length;
loopKP(hand, handVals);
// drawConnections(hand);
// drawHandText(hand);
drawBounds(handVals);
detectGestures(handVals);
debug.innerHTML = `
Hands detected: ${hands.length}<br>
Palm radius: ${handVals.palmRad.toFixed(2)}<br>
Fingers in palm: ${handVals.fingersInPalm}<br>
Hand radius: ${handVals.handRad.toFixed(2)}<br>
Fingers on edge: ${handVals.fingersOnBounds}<br>
Pose: ${handVals.pose}<br>
`;
}
}
function drawConnections(hand) {
for (let j = 0; j < connections.length; j++) {
let pointAIndex = connections[j][0];
let pointBIndex = connections[j][1];
let pointA = hand.keypoints[pointAIndex];
let pointB = hand.keypoints[pointBIndex];
stroke(conf.hands[hand.handedness]);
strokeWeight(conf.kp_conn.stroke);
line(pointA.x, pointA.y, pointB.x, pointB.y);
}
}
function detectGestures(handVals) {
if (handVals.fingersInPalm >= 4) {
stroke(0, 255, 0);
fill(0, 255, 0, 100);
// circle(handVals.palmBounds.c.x, handVals.palmBounds.c.y, handVals.palmRad * 2);
handVals.pose = "🪨";
} else if (handVals.fingersOnBounds == 2 && handVals.fingersInPalm >= 2) {
// && dist(handVals.fingers[0].x, handVals.fingers[0].y, handVals.palmBounds.c.x, handVals.palmBounds.c.y) < handVals.palmRad * 1.2
stroke(255, 0, 0);
fill(255, 0, 0, 100);
handVals.pose = "✂️";
} else if (handVals.fingersInPalm == 0 && handVals.fingersOnBounds >= 3) {
handVals.pose = "📄";
}
textSize(64);
fill(conf.hands[handVals.handedness]);
text(handVals.pose, handVals.palmBounds.c.x - textWidth(handVals.pose) / 2, handVals.palmBounds.c.y);
}
function drawBounds(handVals) {
let x = handVals.handBounds.min.x;
let y = handVals.handBounds.min.y;
let w = handVals.handBounds.max.x - x;
let h = handVals.handBounds.max.y - y;
noFill();
stroke(conf.hands[handVals.handedness]);
strokeWeight(conf.hand_bounds.stroke);
// rect(x, y, w, h);
// circle(handVals.palmBounds.c.x, handVals.palmBounds.c.y, handVals.palmRad * 2);// Palm center
// circle(handVals.fingersBounds.c.x, handVals.fingersBounds.c.y, handVals.fingersRad * 2);// Finger center
// // rect(handVals.fingersBounds.min.x, handVals.fingersBounds.min.y, handVals.fingersBounds.max.x - handVals.fingersBounds.min.x, handVals.fingersBounds.max.y - handVals.fingersBounds.min.y);// Finger bounds
// // rect(handVals.palmBounds.min.x, handVals.palmBounds.min.y, handVals.palmBounds.max.x - handVals.palmBounds.min.x, handVals.palmBounds.max.y - handVals.palmBounds.min.y);// Palm bounds
}
function loopKP(hand, handVals) {
for (let j = 0; j < hand.keypoints.length; j++) {
const kp = hand.keypoints[j];
fill(conf.kps.color);
noStroke();
if (conf.kps.draw) circle(kp.x, kp.y, conf.kps.size);
fill(conf.kp_text.color);
textSize(conf.kp_text.size);
// text(`[${j}]`, kp.x + conf.kp_text.offx, kp.y - conf.kp_text.offy);
}
handVals.fingersOnBounds = fingers.filter(f => f.x == handVals.handBounds.min.x || f.x == handVals.handBounds.max.x || f.y == handVals.handBounds.min.y || f.y == handVals.handBounds.max.y).length;
}
function drawHandText(hand) {
fill(conf.hands[hand.handedness]);
textSize(conf.kp_text.size * 1.5);
let avgX = conf.hands.palm_kps.reduce((sum, i) => sum + hand.keypoints[i].x, 0) / conf.hands.palm_kps.length;
let avgY = conf.hands.palm_kps.reduce((sum, i) => sum + hand.keypoints[i].y, 0) / conf.hands.palm_kps.length;
let offset = (conf.kp_text.size * 1.5) / 2;
text(hand.handedness, avgX - textWidth(hand.handedness) / 2, avgY + offset);
}
function getBounds(arr) {
let b = { min: { x: Infinity, y: Infinity }, max: { x: -Infinity, y: -Infinity }, c: { x: 0, y: 0 } };
for (let i of arr) {
if (i.x < b.min.x) b.min.x = i.x
if (i.y < b.min.y) b.min.y = i.y
if (i.x > b.max.x) b.max.x = i.x
if (i.y > b.max.y) b.max.y = i.y
}
b.c.x = (b.min.x + b.max.x) / 2;
b.c.y = (b.min.y + b.max.y) / 2;
return b;
}
const clamp = (min, num, max) => Math.min(Math.max(num, min), max);