code wiki / (root) / nx_pose_coach.nx

nx_pose_coach.nx source

↩ module page · 72 lines · 3353 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" 7import "nx_vecmath.nx" 8 9func pc_isqrt(v: i64) -> i64 { return vm_isqrt(v) } 10// Euclidean distance between joint at (t1+o1) and (t2+o2), each 3 coords. 11func pc_jdist(t1: *i64, o1: i64, t2: *i64, o2: i64) -> i64 { 12 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] 13 return pc_isqrt(dx*dx + dy*dy + dz*dz) 14} 15// frame-to-frame distance: sum over joints of jdist(ref frame i, user frame j). 16func pc_framedist(ref: *i64, ri: i64, user: *i64, uj: i64, nj: i64) -> i64 { 17 var s: i64 = 0; var j: i64 = 0 18 while j < nj { s = s + pc_jdist(ref, (ri*nj+j)*3, user, (uj*nj+j)*3); j = j + 1 } 19 return s 20} 21// FRAME-ALIGNED score: total deviation (lower=better) + fills jdev[nj] with the max per-joint deviation across 22// frames (so pc_worst_joint names the culprit). Assumes same tempo. 23func pc_score(ref: *i64, user: *i64, T: i64, nj: i64, jdev: *i64) -> i64 { 24 var j0: i64 = 0 25 while j0 < nj { jdev[j0] = 0; j0 = j0 + 1 } 26 var total: i64 = 0; var f: i64 = 0 27 while f < T { 28 var j: i64 = 0 29 while j < nj { 30 let d: i64 = pc_jdist(ref, (f*nj+j)*3, user, (f*nj+j)*3) 31 total = total + d 32 if d > jdev[j] { jdev[j] = d } 33 j = j + 1 34 } 35 f = f + 1 36 } 37 return total 38} 39func pc_worst_joint(jdev: *i64, nj: i64) -> i64 { 40 var best: i64 = 0; var bv: i64 = jdev[0]; var j: i64 = 1 41 while j < nj { if jdev[j] > bv { bv = jdev[j]; best = j } j = j + 1 } 42 return best 43} 44// TEMPO-INVARIANT distance: DTW-align the two tracks (a slower/faster attempt maps onto the reference), so the 45// residual = FORM error only. cost = min-cost monotone alignment of frame distances. cap = T (<= 64 here). 46func pc_dtw(ref: *i64, user: *i64, T: i64, nj: i64) -> i64 { 47 let big: i64 = 4000000000 48 let cost: *i64 = sys_mmap(T * T * 8) as *i64 49 var i: i64 = 0 50 while i < T { 51 var j: i64 = 0 52 while j < T { 53 let fd: i64 = pc_framedist(ref, i, user, j, nj) 54 var best: i64 = big 55 if i == 0 { if j == 0 { best = 0 } } 56 if i > 0 { if cost[(i-1)*T+j] < best { best = cost[(i-1)*T+j] } } 57 if j > 0 { if cost[i*T+(j-1)] < best { best = cost[i*T+(j-1)] } } 58 if i > 0 { if j > 0 { if cost[(i-1)*T+(j-1)] < best { best = cost[(i-1)*T+(j-1)] } } } 59 cost[i*T+j] = fd + best 60 j = j + 1 61 } 62 i = i + 1 63 } 64 return cost[(T-1)*T + (T-1)] 65} 66// overall FORM SCORE in permille (1000 = perfect), from a deviation total + a tolerance scale. 67func pc_form_score(total: i64, samples: i64, tol: i64) -> i64 { 68 if samples <= 0 { return 0 } 69 let mean: i64 = total / samples // mean per-joint-frame deviation 70 if mean >= tol { return 0 } 71 return 1000 - (mean * 1000 / tol) 72}