a-ml commited on
Commit
4e8c7e3
·
verified ·
1 Parent(s): 0805bfe

Update card: demo video, second comparison, video script, Instagram

Browse files
Files changed (1) hide show
  1. README.md +48 -42
README.md CHANGED
@@ -8,11 +8,8 @@ tags:
8
  - face
9
  - coreml
10
  - apple-silicon
11
- - knowledge-distillation
12
  - depth-anything-v2
13
  base_model: depth-anything/Depth-Anything-V2-Large
14
- datasets:
15
- - CelebAMask-HQ
16
  language:
17
  - en
18
  ---
@@ -21,39 +18,54 @@ language:
21
 
22
  Face-specialized monocular depth estimation. One photo in, sharp facial relief out, with no depth sensor.
23
 
24
- General depth models train on scenes and interiors, so they flatten a face into a smooth blob. FaceDepth fine-tunes Depth Anything V2-Large on 30,000 CelebA-HQ faces, distilling the boundary-accurate Depth Pro teacher under three losses guided by CelebAMask-HQ face parsing. It resolves the eyelid crease, the nostril rim, the lip contour, and the hairline.
25
 
26
- Paper and code: [github.com/AristidesAI/FaceDepth](https://github.com/AristidesAI/FaceDepth)
 
 
 
 
 
 
27
 
28
  ## Results
29
 
30
- Measured on 500 held-out CelebA-HQ faces.
31
 
32
- | metric | pretrained DA2-Large | FaceDepth | change |
33
  |---|---|---|---|
34
  | face-region SSI-MAE | 0.02764 | **0.00906** | **-67%** |
35
- | depth-edge F1 vs teacher | 0.717 | **0.882** | **+23%** |
36
- | edge recall vs teacher | 0.745 | **0.873** | +17% |
37
- | edge precision vs teacher | 0.694 | **0.893** | +29% |
 
 
 
 
 
 
38
 
39
- Edge metrics are density-matched at 5% of in-face gradient pixels with a 2-pixel tolerance, so a blurry model cannot win by spreading weak gradients across the face. Both recall and precision rise, so the student finds the teacher's edges and stops inventing edges the teacher does not have.
 
 
40
 
41
  ## Files
42
 
43
  | file | format | size | notes |
44
  |---|---|---|---|
45
- | `FaceDepth_step15792.pt` | PyTorch | 2.5 GB | original best checkpoint, contains `model`, `ema_model`, `conf_head`. Use `ema_model`. |
46
  | `coreml/FaceDepth_fp32.mlpackage` | Core ML | 1.2 GB | unquantized reference, 5.4 fps |
47
- | `coreml/FaceDepth_fp16.mlpackage` | Core ML | 668 MB | **realtime, 40.5 fps, runs on the Neural Engine** |
48
- | `coreml/FaceDepth_int8.mlpackage` | Core ML | 335 MB | 39.4 fps, smallest, runs on the GPU |
 
49
 
50
- All three Core ML exports correlate at 1.00000 against the PyTorch reference. Benchmarks are 392x518 input on an Apple-silicon laptop with `ComputeUnit.ALL`, mean over 20 runs after warmup.
51
 
52
- On this hardware int8 buys a 2x size reduction rather than speed. Its advantage is the app bundle. The ranking may differ on iPhone, where the Neural Engine is relatively stronger, and we have not measured that.
53
 
54
- ## Usage
55
 
56
- ### Core ML (recommended, realtime)
57
 
58
  ```python
59
  import numpy as np, coremltools as ct
@@ -63,10 +75,10 @@ model = ct.models.MLModel("coreml/FaceDepth_fp16.mlpackage",
63
  compute_units=ct.ComputeUnit.ALL)
64
  img = Image.open("face.jpg").convert("RGB").resize((392, 518))
65
  disp = np.asarray(model.predict({"image": img})["depth"]).reshape(518, 392)
66
- # disp is inverse depth: larger = nearer
67
  ```
68
 
69
- Input is a 392x518 portrait RGB image. ImageNet normalization is folded into the graph, so pass raw pixels. Output is relative inverse depth, where larger means nearer.
70
 
71
  ### PyTorch
72
 
@@ -82,58 +94,52 @@ m.load_state_dict(ck["ema_model"])
82
  m = m.to("mps").eval()
83
  ```
84
 
85
- The checkpoint also holds a `conf_head`, a training-time per-pixel confidence head. Inference does not need it and the Core ML exports drop it.
86
 
87
  ### Normalizing the output for display
88
 
89
- The model returns relative inverse depth with an arbitrary scale. Normalize within the face rather than the whole frame. A whole-frame min-max collapses the face's range as soon as a distant background enters the shot, which reads as a black or washed-out face.
90
 
91
  ```python
92
  lo, hi = np.percentile(disp, 2), np.percentile(disp, 98)
93
  norm = np.clip((disp - lo) / (hi - lo), 0, 1) # 1 = nearest
94
  ```
95
 
96
- ## How it was trained
97
-
98
- **Teacher.** Depth Pro at native 1024 px labels 30,000 CelebA-HQ faces, stored as inverse depth. The teacher sets the ceiling on the student's sharpness, and this choice is the dominant lever in the whole recipe.
99
-
100
- **Masks.** The 19 CelebAMask-HQ classes collapse into a head foreground mask, a per-pixel feature weight (eyes and brows highest, nose and lips medium, skin and hair base), and a boundary map of feature edges.
101
 
102
- **Losses.**
103
 
104
- - Foreground-restricted scale-and-shift-invariant trimmed MAE. Aligning scale over the head alone spends the model's dynamic range on facial relief instead of the background.
105
- - Feature-weighted multi-scale gradient matching on the depth residual. This is the term that produces sharpness.
106
- - Confidence-weighted regression with a learned per-pixel weight, so the student discounts pixels the teacher labels unreliably.
107
- - A boundary term at parsed feature edges, pushing crisp depth steps to the lid line, lip, nostril, and hairline.
 
108
 
109
- **Training.** Multi-resolution crops at 518, 700, and 910 px. AdamW, head and encoder learning rates 2e-5 and 2e-6, cosine schedule, gradient clipping at 1.0, fp32, gradient checkpointing, EMA 0.999. 17,000 steps on one Apple-silicon laptop, stopped on a validation plateau.
110
 
111
  ## Limitations
112
 
113
- This model optimizes single-image sharpness. The temporal-consistency loss came out of the recipe to get there, so live video flickers more than a temporally trained model would. For offline video, smooth the normalization range across frames, and optionally the depth itself, which trades flicker for ghosting under fast motion.
114
-
115
- Distillation caps detail at the teacher. The claim here is sharp feature relief and boundaries, not sub-millimeter texture, and per-eyelash depth is beyond what any current monocular teacher resolves.
116
 
117
- Training data is CelebA-HQ, which is centered, well-lit, and limited in pose, occlusion, and demographic diversity relative to in-the-wild use. Performance by demographic group has not been measured. Evaluate before deploying on populations or capture conditions that differ from CelebA-HQ.
118
 
119
- Loss-term and resolution ablations have not been run, so the contribution attributed to each individual loss term rests on the design argument rather than measured deltas.
120
 
121
  ## License and provenance
122
 
123
  Released under **CC-BY-NC-4.0**, non-commercial research use.
124
 
125
- This is a derivative of [Depth Anything V2-Large](https://huggingface.co/depth-anything/Depth-Anything-V2-Large), which is CC-BY-NC-4.0. It trains on pseudo-labels from [Apple Depth Pro](https://github.com/apple/ml-depth-pro) and on [CelebAMask-HQ](https://github.com/switchablenorms/CelebAMask-HQ), whose terms restrict use to non-commercial research and education. Honor the upstream terms of all three.
126
 
127
  ## Citation
128
 
129
  ```bibtex
130
  @software{facedepth2026,
131
- title = {FaceDepth: Face-Specialized Monocular Depth by Distilling a
132
- Boundary-Accurate Teacher under Segmentation-Guided Losses},
133
  author = {Lintzeris, Aristides},
134
  year = {2026},
135
  url = {https://huggingface.co/a-ml/FaceDepth}
136
  }
137
  ```
138
 
139
- Please also cite the upstream work this builds on: Depth Anything V2 (arXiv:2406.09414), Depth Pro (arXiv:2410.02073), MiDaS (arXiv:1907.01341), DINOv2 (arXiv:2304.07193), DPT (arXiv:2103.13413), and CelebAMask-HQ (arXiv:1907.11922).
 
8
  - face
9
  - coreml
10
  - apple-silicon
 
11
  - depth-anything-v2
12
  base_model: depth-anything/Depth-Anything-V2-Large
 
 
13
  language:
14
  - en
15
  ---
 
18
 
19
  Face-specialized monocular depth estimation. One photo in, sharp facial relief out, with no depth sensor.
20
 
21
+ General depth models train on streets, rooms, and furniture, so they flatten a face into a smooth blob. A face holds its information in millimeters of relief, the bridge of the nose against the cheek, the small step from lid to eyeball, and that signal sits far below what a scene-trained model represents. Face relighting, avatar capture, and AR effects need the part those models throw away.
22
 
23
+ FaceDepth is a Depth Anything V2-Large variant tuned to keep that detail. It resolves the eyelid crease, the nostril rim, the lip contour, and the hairline.
24
+
25
+ Code and paper: [github.com/AristidesAI/FaceDepth](https://github.com/AristidesAI/FaceDepth) · Instagram: [@aristides.lab](https://instagram.com/aristides.lab)
26
+
27
+ <video controls autoplay loop muted playsinline src="https://huggingface.co/a-ml/FaceDepth/resolve/main/figures/facedepth_video_demo.mp4"></video>
28
+
29
+ *Source on the left, FaceDepth on the right.*
30
 
31
  ## Results
32
 
33
+ Measured on 500 held-out faces.
34
 
35
+ | metric | baseline DA2-Large | FaceDepth | change |
36
  |---|---|---|---|
37
  | face-region SSI-MAE | 0.02764 | **0.00906** | **-67%** |
38
+ | depth-edge F1 vs reference | 0.717 | **0.882** | **+23%** |
39
+ | edge recall vs reference | 0.745 | **0.873** | +17% |
40
+ | edge precision vs reference | 0.694 | **0.893** | +29% |
41
+
42
+ Edge metrics are density-matched at 5% of in-face gradient pixels with a 2-pixel tolerance, so a blurry model cannot win by spreading weak gradients across the whole face. Recall and precision both rise, so the model finds real depth edges and stops inventing ones that are not there.
43
+
44
+ ![input, reference, baseline, FaceDepth, and difference for six held-out faces](https://huggingface.co/a-ml/FaceDepth/resolve/main/figures/comparison.jpg)
45
+
46
+ *Left to right: input, reference depth, baseline DA2-Large, FaceDepth, and the difference between the last two. Depth is normalized inside the face mask so relief stays visible. Compare columns three and four against column two.*
47
 
48
+ ![a second set of six held-out faces in the same layout](https://huggingface.co/a-ml/FaceDepth/resolve/main/figures/comparison_2.jpg)
49
+
50
+ *Six more held-out faces, same layout. The difference column concentrates on the face interior and hairline.*
51
 
52
  ## Files
53
 
54
  | file | format | size | notes |
55
  |---|---|---|---|
56
+ | `FaceDepth_step15792.pt` | PyTorch | 2.5 GB | full checkpoint. Use the `ema_model` key. |
57
  | `coreml/FaceDepth_fp32.mlpackage` | Core ML | 1.2 GB | unquantized reference, 5.4 fps |
58
+ | `coreml/FaceDepth_fp16.mlpackage` | Core ML | 668 MB | **realtime, 40.5 fps, Neural Engine** |
59
+ | `coreml/FaceDepth_int8.mlpackage` | Core ML | 335 MB | 39.4 fps, smallest, GPU |
60
+ | `video_depth.py` | script | | convert any video into a depth-map video |
61
 
62
+ Every Core ML export correlates at 1.00000 against the PyTorch reference. Benchmarks use 392x518 input on an Apple-silicon laptop with `ComputeUnit.ALL`, averaged over 20 runs after warmup.
63
 
64
+ On this hardware int8 buys a 2x size reduction rather than speed, so its advantage is the app bundle. The ranking may differ on iPhone, where the Neural Engine is relatively stronger, and that has not been measured.
65
 
66
+ ## Quick start
67
 
68
+ ### Core ML, realtime
69
 
70
  ```python
71
  import numpy as np, coremltools as ct
 
75
  compute_units=ct.ComputeUnit.ALL)
76
  img = Image.open("face.jpg").convert("RGB").resize((392, 518))
77
  disp = np.asarray(model.predict({"image": img})["depth"]).reshape(518, 392)
78
+ # inverse depth: larger = nearer
79
  ```
80
 
81
+ Input is a 392x518 portrait RGB image. ImageNet normalization is folded into the graph, so pass raw pixels.
82
 
83
  ### PyTorch
84
 
 
94
  m = m.to("mps").eval()
95
  ```
96
 
97
+ The checkpoint also carries a `conf_head`. Inference does not need it and the Core ML exports drop it.
98
 
99
  ### Normalizing the output for display
100
 
101
+ The model returns relative inverse depth with an arbitrary scale. **Normalize inside the face, not across the frame.** A whole-frame min-max collapses the face's range the moment a distant background enters the shot, which reads as a black or washed-out face. This one detail causes most of the "the model looks broken" reports.
102
 
103
  ```python
104
  lo, hi = np.percentile(disp, 2), np.percentile(disp, 98)
105
  norm = np.clip((disp - lo) / (hi - lo), 0, 1) # 1 = nearest
106
  ```
107
 
108
+ ## Video
 
 
 
 
109
 
110
+ `video_depth.py` converts any video ffmpeg can read, up to 4K, into a depth-map video. Frames stream through an ffmpeg pipe, so a long clip never lands on disk as a frame dump and memory stays flat.
111
 
112
+ ```bash
113
+ python video_depth.py --input clip.mov --output depth.mp4 --ckpt FaceDepth_step15792.pt
114
+ python video_depth.py --input clip.mov --output sbs.mp4 --side-by-side --colormap magma
115
+ python video_depth.py --input clip.mp4 --output out.mp4 --smooth-depth 0.3 --bf16
116
+ ```
117
 
118
+ Colormaps: inferno, magma, turbo, viridis, plasma, bone, gray. `--range-ema` smooths the near/far range across frames and is on by default with no ghosting. `--smooth-depth` smooths depth itself, which cuts residual jitter but ghosts behind fast motion. Needs ffmpeg on PATH and [Depth-Anything-V2](https://github.com/DepthAnything/Depth-Anything-V2) cloned into `third_party/DepthAnythingV2`.
119
 
120
  ## Limitations
121
 
122
+ This model optimizes single-image sharpness, so live video flickers more than a temporally stabilized model would. For offline video, `video_depth.py` damps that with `--range-ema` and `--smooth-depth`.
 
 
123
 
124
+ Detail is capped by the reference the model learned from. The claim is sharp feature relief and boundaries, not sub-millimeter texture. Per-eyelash depth is beyond what any current monocular model resolves.
125
 
126
+ Training data is centered, well-lit, and limited in pose, occlusion, and demographic diversity relative to in-the-wild use. Performance by demographic group has not been measured. Evaluate before deploying on populations or capture conditions that differ from the training distribution.
127
 
128
  ## License and provenance
129
 
130
  Released under **CC-BY-NC-4.0**, non-commercial research use.
131
 
132
+ FaceDepth derives from [Depth Anything V2-Large](https://huggingface.co/depth-anything/Depth-Anything-V2-Large), which is CC-BY-NC-4.0. It builds on [Apple Depth Pro](https://github.com/apple/ml-depth-pro) and [CelebAMask-HQ](https://github.com/switchablenorms/CelebAMask-HQ), whose terms restrict use to non-commercial research and education. Honor the upstream terms of all three.
133
 
134
  ## Citation
135
 
136
  ```bibtex
137
  @software{facedepth2026,
138
+ title = {FaceDepth: Face-Specialized Monocular Depth Estimation},
 
139
  author = {Lintzeris, Aristides},
140
  year = {2026},
141
  url = {https://huggingface.co/a-ml/FaceDepth}
142
  }
143
  ```
144
 
145
+ Please also cite the work this builds on: Depth Anything V2 (arXiv:2406.09414), Depth Pro (arXiv:2410.02073), MiDaS (arXiv:1907.01341), DPT (arXiv:2103.13413), DINOv2 (arXiv:2304.07193), and CelebAMask-HQ (arXiv:1907.11922).