code wiki / (root) / nx_pose_coach.nx

nx_pose_coach.nx source

↩ module page · 71 lines · 3428 B

1// nx_pose_coach.nx -- the COACH's read (roadmap R8): compare a USER pose track to a REFERENCE track and say 2// WHAT is off (which joint, how far) and score the form -- with TEMPO-INVARIANT alignment (DTW) so a slower/ 3// faster attempt isn't penalised for form. Pure integer math on joint-position tracks (track[f*nj*3 + j*3 + c]); 4// the tracks come from R3-retarget (from video via R2) or the rig. This is "help people achieve their goals": 5// name the error, not just a number. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7 8func pc_isqrt(v: i64) -> i64 { if v <= 0 { return 0 } var x: i64 = v; var y: i64 = (x + 1) / 2; while y < x { x = y; y = (x + v / x) / 2 } return x } 9// Euclidean distance between joint at (t1+o1) and (t2+o2), each 3 coords. 10func pc_jdist(t1: *i64, o1: i64, t2: *i64, o2: i64) -> i64 { 11 let dx: i64 = t1[o1] - t2[o2]; let dy: i64 = t1[o1+1] - t2[o2+1]; let dz: i64 = t1[o1+2] - t2[o2+2] 12 return pc_isqrt(dx*dx + dy*dy + dz*dz) 13} 14// frame-to-frame distance: sum over joints of jdist(ref frame i, user frame j). 15func pc_framedist(ref: *i64, ri: i64, user: *i64, uj: i64, nj: i64) -> i64 { 16 var s: i64 = 0; var j: i64 = 0 17 while j < nj { s = s + pc_jdist(ref, (ri*nj+j)*3, user, (uj*nj+j)*3); j = j + 1 } 18 return s 19} 20// FRAME-ALIGNED score: total deviation (lower=better) + fills jdev[nj] with the max per-joint deviation across 21// frames (so pc_worst_joint names the culprit). Assumes same tempo. 22func pc_score(ref: *i64, user: *i64, T: i64, nj: i64, jdev: *i64) -> i64 { 23 var j0: i64 = 0 24 while j0 < nj { jdev[j0] = 0; j0 = j0 + 1 } 25 var total: i64 = 0; var f: i64 = 0 26 while f < T { 27 var j: i64 = 0 28 while j < nj { 29 let d: i64 = pc_jdist(ref, (f*nj+j)*3, user, (f*nj+j)*3) 30 total = total + d 31 if d > jdev[j] { jdev[j] = d } 32 j = j + 1 33 } 34 f = f + 1 35 } 36 return total 37} 38func pc_worst_joint(jdev: *i64, nj: i64) -> i64 { 39 var best: i64 = 0; var bv: i64 = jdev[0]; var j: i64 = 1 40 while j < nj { if jdev[j] > bv { bv = jdev[j]; best = j } j = j + 1 } 41 return best 42} 43// TEMPO-INVARIANT distance: DTW-align the two tracks (a slower/faster attempt maps onto the reference), so the 44// residual = FORM error only. cost = min-cost monotone alignment of frame distances. cap = T (<= 64 here). 45func pc_dtw(ref: *i64, user: *i64, T: i64, nj: i64) -> i64 { 46 let big: i64 = 4000000000 47 let cost: *i64 = sys_mmap(T * T * 8) as *i64 48 var i: i64 = 0 49 while i < T { 50 var j: i64 = 0 51 while j < T { 52 let fd: i64 = pc_framedist(ref, i, user, j, nj) 53 var best: i64 = big 54 if i == 0 { if j == 0 { best = 0 } } 55 if i > 0 { if cost[(i-1)*T+j] < best { best = cost[(i-1)*T+j] } } 56 if j > 0 { if cost[i*T+(j-1)] < best { best = cost[i*T+(j-1)] } } 57 if i > 0 { if j > 0 { if cost[(i-1)*T+(j-1)] < best { best = cost[(i-1)*T+(j-1)] } } } 58 cost[i*T+j] = fd + best 59 j = j + 1 60 } 61 i = i + 1 62 } 63 return cost[(T-1)*T + (T-1)] 64} 65// overall FORM SCORE in permille (1000 = perfect), from a deviation total + a tolerance scale. 66func pc_form_score(total: i64, samples: i64, tol: i64) -> i64 { 67 if samples <= 0 { return 0 } 68 let mean: i64 = total / samples // mean per-joint-frame deviation 69 if mean >= tol { return 0 } 70 return 1000 - (mean * 1000 / tol) 71}