Anny/MHR body loaders and ISO 8559-1 body measurements
Project description
clad-body
ISO 8559-1 body measurements for Anny and MHR parametric body models. Nine keys are differentiable through PyTorch autograd for gradient-based body fitting.
Anny and MHR give you a 14–18K vertex mesh and nothing to measure it with. SMPL tooling doesn't port over, and the plane-sweep algorithms look simple until you hit convex-hull tape simulation, contour-fragment merging, and ISO-compliant landmark detection for bust/hip/crotch. clad-body is that work, done once — 25 anthropometric measurements over circumferences, lengths, and body composition (volume, mass, BMI, body fat), calibrated against real scan data. It's used in production at Clad for size-aware virtual try-on.
All bodies are normalised to the same coordinate convention: Z-up, metres, XY-centred, feet at Z=0, +Y=front.
Install
pip install clad-body
# With Anny body loader (requires torch)
pip install 'clad-body[anny]'
# With MHR body loader (requires pymomentum)
pip install 'clad-body[mhr]'
# With 4-view rendering
pip install 'clad-body[render]'
Quick start
from clad_body.load import load_anny_from_params
from clad_body.measure import measure
body = load_anny_from_params(params)
m = measure(body) # all measurements
m = measure(body, preset="core") # 4: height, bust, waist, hip
m = measure(body, preset="standard") # 9: + thigh, upperarm, shoulder, sleeve, inseam
m = measure(body, preset="tops") # garment-relevant subset
m = measure(body, only=["bust_cm", "hip_cm"]) # specific keys
m = measure(body, tags={"type": "circumference", "region": "leg"}) # tag filter
m = measure(body, render_path="body.png") # with 4-view render
MHR works the same way:
from clad_body.load import load_mhr_from_params
body = load_mhr_from_params("path/to/sam3d_params.json")
m = measure(body)
Differentiable path — measure_grad (Anny only, experimental)
Under active development. API surface and supported keys may change between minor versions. Nine keys are differentiable today; more will follow.
For autograd-based optimization of the body mesh, use measure_grad(body) instead of measure(body). Same input, same key names — but the returned values are PyTorch tensors with autograd history, so you can put them directly into a loss and backprop into the Anny phenotype parameters.
Pass requires_grad=True to load_anny_from_params to create the body with gradient-enabled phenotype tensors (stored on body.phenotype_kwargs):
import torch
from clad_body.load import load_anny_from_params
from clad_body.measure import measure_grad
body = load_anny_from_params(initial_params, requires_grad=True)
optimizer = torch.optim.Adam(list(body.phenotype_kwargs.values()), lr=0.01)
for step in range(500):
optimizer.zero_grad()
m = measure_grad(body, only=["bust_cm", "waist_cm", "inseam_cm"])
loss = (m["bust_cm"] - 92.0) ** 2 + (m["waist_cm"] - 78.0) ** 2 + (m["inseam_cm"] - 82.0) ** 2
loss.backward()
optimizer.step()
Each measure_grad(body) call re-runs the forward pass using body.phenotype_kwargs, so after optimizer.step() updates the tensors the next iteration measures the new mesh. If you only want to optimize a subset of parameters, load without requires_grad=True and enable it per-tensor: body.phenotype_kwargs["height"].requires_grad_(True).
Supported keys and their calibration error vs the ISO reference that measure() uses:
| Key | Error vs ISO |
|---|---|
height_cm, waist_cm |
exact (same loop / extent) |
bust_cm |
MAE 0.06 cm, max 0.18 cm |
underbust_cm |
MAE 0.39 cm, max 1.61 cm |
inseam_cm |
RMS 0.06 cm, max 0.10 cm |
sleeve_length_cm |
RMS 0.33 cm, max 0.55 cm |
upperarm_cm |
≤ 1 cm |
mass_kg |
≤ 3 kg |
thigh_cm |
broken — gradient direction only (vertex loop under-reports by 3–6 cm; use measure() for reporting) |
Circumference = convex hull perimeter, not contour perimeter
Both measure() and measure_grad report convex hull circumference, not the raw cross-section perimeter. This matches ISO 8559-1: a real measuring tape bridges across concavities (e.g., cleavage between breasts, armpit crease) rather than dipping into them. The convex hull perimeter is always ≤ the raw contour perimeter — the difference is most visible at the bust on larger cup sizes where the cleavage concavity can shorten the measurement by 1-3 cm compared to following the actual surface.
measure_grad builds a 72-point polygon via differentiable soft edge-plane intersection, then takes its scipy.spatial.ConvexHull perimeter. Every measure_grad() call recomputes the hull from scratch (new forward pass → new polygon → new hull). The hull decides which polygon vertices to keep (discrete, like argmax or tensor[mask]) — but the perimeter of those vertices is a plain sum of torch.linalg.norm over their positions, so loss.backward() flows gradients through the kept vertices back to the Anny phenotype params. Dropped vertices (inside the hull, e.g., cleavage bins) get zero gradient — correct, since they don't affect the tape measure. The hull indices can change between optimization steps if the body shape changes enough (e.g., breasts flatten and the cleavage disappears), but the perimeter value is continuous at those transitions so the loss doesn't jump. In practice the hull is very stable: ~3-5 bins are consistently concave (cleavage), the rest consistently on the hull. See clad_body/measure/_soft_circ.py.
Requesting any other key raises ValueError. There is no silent numpy fallback — it would break gradient flow without warning. For non-differentiable keys use measure().
Public API
| Import | What |
|---|---|
clad_body.load.load_anny_from_params |
Load Anny body from phenotype params |
clad_body.load.load_mhr_from_params |
Load MHR body from SAM 3D Body params |
clad_body.load.AnnyBody, MhrBody |
Body dataclasses |
clad_body.measure.measure |
Measure a body (numpy reporting path, ISO 8559-1) |
clad_body.measure.measure_grad |
Differentiable measurements for autograd loops (Anny only) |
clad_body.measure.REGISTRY |
All measurement definitions (dict[str, MeasurementDef]) |
clad_body.measure.list_measurements |
Query measurements by tags |
clad_body.measure.MeasurementDef |
Measurement definition type |
Selection
measure() accepts preset, only, tags, exclude. Precedence: only > preset > tags > default ("all"). exclude is applied last. Only runs computation groups needed for the requested keys.
Introspection
from clad_body.measure import REGISTRY, list_measurements
REGISTRY["bust_cm"].description # self-measurement instructions
REGISTRY["bust_cm"].iso_ref # "5.3.4"
REGISTRY["bust_cm"].type # "circumference"
list_measurements(type="circumference", region="leg") # [thigh, knee, calf]
Measurement registry
Every measurement is tagged across 5 dimensions. Each carries a human-readable description for self-measurement instructions and i18n key mapping.
Tags
| Dimension | Values |
|---|---|
| type | circumference, length, scalar |
| standard | iso (ISO 8559-1), tailor (industry standard), derived (computed) |
| region | neck, torso, abdomen, arm, leg, full_body |
| tier | core > standard > enhanced > fitted (cumulative) |
| garments | tops, bottoms, dresses, outerwear, underwear |
Tier presets
| Preset | Count | Adds |
|---|---|---|
core |
4 | height, bust, waist, hip |
standard |
9 | thigh, upperarm, shoulder_width, sleeve_length, inseam |
enhanced |
18 | neck, underbust, stomach, mass, volume, bmi, body_fat, belly_depth, back_neck_to_waist |
fitted/all |
25 | knee, calf, wrist, crotch_length, front_rise, back_rise, shirt_length |
Full measurement table
Garment codes: Tops, Bottoms, Dresses, Outerwear, Underwear.
| Contour | Key | Description | ISO | Type | Std | Region | Tier | Grp | Gar |
|---|---|---|---|---|---|---|---|---|---|
height_cm |
Vertical distance from floor to top of head. Stand erect, feet together. | 5.1.1 | scalar | iso | full_body | core | A | all | |
bust_cm |
Horizontal circumference at the fullest part of the chest/bust. Tape under armpits, across bust prominence, level and snug. | 5.3.4 | circ | iso | torso | core | A | T,D,O,U | |
waist_cm |
Horizontal circumference at natural waist, midway between lowest rib and hip bone. Tape at navel height, parallel to floor. | 5.3.10 | circ | iso | torso | core | A | all | |
hip_cm |
Horizontal circumference at greatest buttock prominence. Feet together, tape around widest part of hips. | 5.3.13 | circ | iso | abdomen | core | A | B,D,O,U | |
thigh_cm |
Horizontal circumference at fullest part of upper thigh, just below gluteal fold. Stand with legs slightly apart. | 5.3.20 | circ | iso | leg | std | B | B | |
upperarm_cm |
Circumference at fullest part of upper arm, midway between shoulder and elbow. Arm relaxed, not flexed. | 5.3.16 | circ | iso | arm | std | B | T,O | |
shoulder_width_cm |
Distance between left and right shoulder points (acromion), measured across back over C7 vertebra. | 5.4.2 | length | iso | torso | std | C | T,D,O | |
sleeve_length_cm |
Distance from shoulder point along outside of slightly bent arm, over elbow, to wrist bone. (ISO §5.4.14 + §5.4.15 outer arm length, computed via plane-slice surface walk on rest pose; differentiable runtime is bone chain + linear correction.) | 5.7.8 | length | iso | arm | std | C | T,O | |
inseam_cm |
Distance from crotch point straight down to floor. Stand erect, feet slightly apart. | 5.1.15 | length | iso | leg | std | E | B | |
neck_cm |
Circumference just below Adam's apple, perpendicular to neck axis. Comfortably snug. | 5.3.2 | circ | iso | neck | enh | D | T | |
underbust_cm |
Horizontal circumference directly below breast tissue, at inframammary crease. Bra band size. | 5.3.6 | circ | iso | torso | enh | A | T,D,U | |
stomach_cm |
Horizontal circumference at maximum anterior protrusion of abdomen, usually at/below navel. | -- | circ | tailor | abdomen | enh | A | T,B | |
mass_kg |
Total body mass in kilograms. | 5.6.1 | scalar | iso | full_body | enh | G | -- | |
volume_m3 |
Total body volume in cubic metres, from mesh geometry. | -- | scalar | derived | full_body | enh | G | -- | |
bmi |
Body mass index: mass (kg) / height (m)^2. | -- | scalar | derived | full_body | enh | G | -- | |
body_fat_pct |
Estimated body fat % via Navy/Weltman equations from circumferences. | -- | scalar | derived | full_body | enh | G | -- | |
belly_depth_cm |
How much belly protrudes forward vs underbust/ribcage. Negative = belly prominence. | -- | scalar | derived | abdomen | enh | A | T,B | |
knee_cm |
Horizontal circumference at centre of kneecap. Bend knee slightly (~45 degrees). | 5.3.22 | circ | iso | leg | fit | B | B | |
calf_cm |
Maximum horizontal circumference of the calf. Stand with legs slightly apart. | 5.3.24 | circ | iso | leg | fit | B | B | |
wrist_cm |
Circumference at wrist, at prominent bone on little finger side (ulnar styloid). | 5.3.19 | circ | iso | arm | fit | D | T | |
crotch_length_cm |
Distance from front waist centre, through crotch, to back waist centre. Follow body surface. | 5.4.18 | length | iso | leg | fit | E | B | |
front_rise_cm |
Front waist to crotch point, along front body surface. Trouser front panel length. | -- | length | tailor | leg | fit | E | B | |
back_rise_cm |
Back waist to crotch point, along back body surface. Trouser back panel length. | -- | length | tailor | leg | fit | E | B | |
shirt_length_cm |
Side neck point down along front body contour to crotch level. Follow chest/stomach curve. | -- | length | tailor | torso | fit | F | T | |
back_neck_to_waist_cm |
Cervicale (C7) down centre back along body contour to waist level. Tape follows spine curvature. | 5.4.5 | length | iso | torso | enh | H | T,D,O |
Tier codes: core, std (standard), enh (enhanced), fit (fitted). Anny-only: underbust, mass, volume, bmi, body_fat, belly_depth.
Computation groups
| Group | Measurements | Cost | Deps |
|---|---|---|---|
| A Core torso | height, bust, waist, hip, stomach, underbust, belly_depth | Cheap | -- |
| B Limb sweeps | thigh, knee, calf, upperarm | Expensive | -- |
| C Joint linear | shoulder_width, sleeve_length (ISO surface walk on re-posed body) | Very expensive | -- |
| D Perpendicular | neck, wrist | Medium | -- |
| E Mesh geometry | inseam (mesh sweep), crotch_length, front_rise, back_rise | Medium | -- |
| F Surface trace | shirt_length | Medium | E |
| G Body composition | volume, mass, bmi, body_fat | Cheap | D |
| H Back length | back_neck_to_waist | Cheap | A |
Group C and E have differentiable alternatives in measure_grad — use it for hot-loop optimization instead of calling measure() repeatedly.
Performance
measure() only runs the computation groups needed for the requested keys — use only= or preset= to skip expensive groups:
measure(body) # all groups — ~800 ms
measure(body, preset="core") # group A only — ~100 ms
measure(body, only=["bust_cm"]) # group A only — ~100 ms
measure(body, only=["shoulder_width_cm"]) # groups A + C — ~200 ms
GPU acceleration
measure() accepts a device parameter (None = auto-detect CUDA):
measure(body, only=["bust_cm"], device="cuda") # GPU forward pass
measure(body, device=None) # auto: CUDA if available
Optional extras
| Extra | What it enables |
|---|---|
[anny] |
Anny body loader (requires torch) |
[mhr] |
MHR body loader (requires pymomentum) |
[render] |
4-view body renders (requires matplotlib, pyrender) |
Without extras, only numpy, scipy, and trimesh are required.
Demo
Try the full pipeline at clad.you/size-aware/size-me.
Background
This library was built for Clad's size-aware virtual try-on pipeline. Read the full story: A 3D Body Scan for Nine Cents — Without SMPL.
License
Apache 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file clad_body-0.4.2.tar.gz.
File metadata
- Download URL: clad_body-0.4.2.tar.gz
- Upload date:
- Size: 182.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de223e5cf6ed69b2aabf99b3ec144a9ef4b6ad4a9abc2308a07d20c7563728a7
|
|
| MD5 |
3bad1f36765fead70e6e46461cff16e0
|
|
| BLAKE2b-256 |
5913f0221cce2adc89f95ea0c457e66f70901e8138d4864c12f5f6ddb1319116
|
Provenance
The following attestation bundles were made for clad_body-0.4.2.tar.gz:
Publisher:
ci.yml on datar-psa/clad-body
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
clad_body-0.4.2.tar.gz -
Subject digest:
de223e5cf6ed69b2aabf99b3ec144a9ef4b6ad4a9abc2308a07d20c7563728a7 - Sigstore transparency entry: 1287187848
- Sigstore integration time:
-
Permalink:
datar-psa/clad-body@d46895cc6414a8605b12f7b40305fff33f5fb697 -
Branch / Tag:
refs/tags/clad-body/v0.4.2 - Owner: https://github.com/datar-psa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d46895cc6414a8605b12f7b40305fff33f5fb697 -
Trigger Event:
push
-
Statement type:
File details
Details for the file clad_body-0.4.2-py3-none-any.whl.
File metadata
- Download URL: clad_body-0.4.2-py3-none-any.whl
- Upload date:
- Size: 116.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc8820ed843dfe7fe06bd21b0dd5fb13c45c1d93aa0c5642f4c58a53ddaf51e3
|
|
| MD5 |
ad0f4b3b71803afbd21e01a487677c45
|
|
| BLAKE2b-256 |
ed6dffac6d859d666dcd26ac150ee3ce68ece6ff7c867e1e610ac284b4a685d7
|
Provenance
The following attestation bundles were made for clad_body-0.4.2-py3-none-any.whl:
Publisher:
ci.yml on datar-psa/clad-body
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
clad_body-0.4.2-py3-none-any.whl -
Subject digest:
dc8820ed843dfe7fe06bd21b0dd5fb13c45c1d93aa0c5642f4c58a53ddaf51e3 - Sigstore transparency entry: 1287188050
- Sigstore integration time:
-
Permalink:
datar-psa/clad-body@d46895cc6414a8605b12f7b40305fff33f5fb697 -
Branch / Tag:
refs/tags/clad-body/v0.4.2 - Owner: https://github.com/datar-psa
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d46895cc6414a8605b12f7b40305fff33f5fb697 -
Trigger Event:
push
-
Statement type: