@@ -9,8 +9,8 @@ against real **DXA scans**. Everything runs on the device — no upload, no API.
99<p align =" center " ><sub >Demo recorded from the real app using its built-in sample body — a real BodyM subject with
1010known tape measurements. Live camera capture isn't shown. <a href =" https://rishabcv-eng.github.io/bodycomp/ " >Try it live →</a ></sub ></p >
1111
12- ** 2.82 % mean absolute error against clinical DXA** , beating the standard
13- tape-measure formula (3.17%) without needing a tape.
12+ ** 2.84 % mean absolute error against clinical DXA** in good conditions, 2.96% in
13+ harder ones — at or below the best tape-measure formula (3.16%), without the tape.
1414
1515```
1616photo pair ──▶ segmentation ──▶ Stage 1 ──▶ circumferences ──▶ Stage 2 ──▶ body fat %, lean mass
@@ -19,7 +19,7 @@ photo pair ──▶ segmentation ──▶ Stage 1 ──▶ circumferences ─
1919
2020| | |
2121| ---| ---|
22- | ** Body fat, end to end** | 2.82 % MAE vs DXA |
22+ | ** Body fat, end to end** | 2.84 % MAE vs DXA |
2323| ** Waist from silhouette** | 1.97 cm MAE |
2424| ** Interval coverage** | 80.5% (claimed 80%) |
2525| ** Model size / latency** | 1.4 MB, ~ 16 ms in-browser |
@@ -92,6 +92,7 @@ is how wrong numbers ship:
9292| ` scan_steps_test.mjs ` | stepped capture state machine | 18 checks pass |
9393| ` overlay_test.mjs ` | contour, mesh and chord geometry | 26 checks pass |
9494| ` plan_test.mjs ` | plan maths and safety floors | 30 checks pass |
95+ | ` progress_test.mjs ` | history, crew board, goals, ranking | 61 checks pass |
9596
9697### A bug worth keeping
9798
@@ -106,6 +107,39 @@ above the ankles, it has lost the legs. Both implementations now carry the check
106107and both reject the photo.
107108
108109
110+ ## Closing the generalisation gap with augmentation
111+
112+ The honest weakness of the first build: Stage 1 beat the height-and-weight baseline
113+ by 41% on testA but only ** 3% on testB** — different subjects, harder conditions.
114+ The model leaned on clean capture.
115+
116+ The fix came from an earlier measurement. ` reports/mask_robustness.csv ` had already
117+ established * which* mask defects cost accuracy at inference; ` src/augmented_stage1.py `
118+ trains on those same defects — dilation, erosion, boundary noise, a mis-thresholded
119+ matte — so the model meets imperfect masks during fitting instead of first meeting
120+ one in the wild. Test splits stay clean, so the comparison is like for like.
121+
122+ | testB (400 subjects) | clean-only | + augmented | |
123+ | ---| ---| ---| ---|
124+ | Waist | 3.245 | ** 3.033** | −0.213 cm |
125+ | Hip | 2.226 | ** 2.108** | −0.119 cm |
126+ | Chest | 2.760 | ** 2.675** | −0.085 cm |
127+ | Thigh | 1.702 | ** 1.586** | −0.117 cm |
128+ | Upper arm | 1.226 | ** 1.220** | −0.006 cm |
129+
130+ ** Every measurement improved on the harder split** , and four of five on testA too
131+ (waist there cost +0.05 cm — the one trade).
132+
133+ Retrained at full size, the gap narrows further: waist on testB goes from 3.18 cm to
134+ ** 2.95 cm** , turning a 3% gain over baseline into ** 10.4%** . Hip improves from 11% to
135+ ** 21%** , thigh to ** 17%** .
136+
137+ What it does * not* do is move the end-to-end body-fat number much (2.82% → 2.84% on
138+ testA): Stage 2's own error dominates once measurements are this close. Better
139+ measurements are still worth having — they are what the plan, the ranking and the
140+ trend are built on — but the honest summary is that this fixed Stage 1's weakest
141+ result, not the headline. The shipped app uses these models.
142+
109143## A leaderboard that costs no privacy
110144
111145A shared leaderboard needs accounts, a server and a database of other people's body
0 commit comments