bdck commited on
Commit
c4512e1
Β·
verified Β·
1 Parent(s): dbd80e5

Upload README.md

Browse files
Files changed (1) hide show
  1. README.md +294 -0
README.md ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DepthPro Wrapper β€” Image to Point Cloud
2
+
3
+ [![Paper](https://img.shields.io/badge/Paper-arXiv%3A2410.02073-blue)](https://arxiv.org/abs/2410.02073)
4
+ [![Model](https://img.shields.io/badge/πŸ€—%20Model-apple%2FDepthPro--hf-yellow)](https://huggingface.co/apple/DepthPro-hf)
5
+ [![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
6
+
7
+ A **clean, drop-in Python wrapper** around Apple's **DepthPro** (arXiv:2410.02073) that turns a single RGB image into a **metric depth map** and, if you want, a **3D point cloud** β€” with **zero calibration**.
8
+
9
+ DepthPro is a 952 M-parameter ViT-L model that predicts **absolute metric depth** (meters, not relative) and estimates the **camera focal length** and **field of view** automatically. No camera intrinsics, no per-scene training, no LiDAR required.
10
+
11
+ ---
12
+
13
+ ## πŸš€ Quick start (10 lines)
14
+
15
+ ```python
16
+ from depthpro_wrapper import DepthProEstimator, rgbd_to_point_cloud, save_point_cloud
17
+
18
+ # 1. Load model (~2 GB download on first run)
19
+ estimator = DepthProEstimator(device="cuda:0")
20
+
21
+ # 2. Drop an image in
22
+ result = estimator.estimate("photo.jpg")
23
+ print(f"Focal length: {result.focal_length:.1f} px")
24
+ print(f"Depth range: {result.depth.min():.2f} – {result.depth.max():.2f} m")
25
+
26
+ # 3. Get a coloured point cloud out
27
+ points, colors = rgbd_to_point_cloud(
28
+ result.depth, result.image, result.focal_length
29
+ )
30
+
31
+ # 4. Save as PLY
32
+ save_point_cloud("scene.ply", points, colors=colors)
33
+ ```
34
+
35
+ Or use the CLI:
36
+
37
+ ```bash
38
+ python scripts/image_to_pointcloud.py photo.jpg scene.ply --colored --normals
39
+ ```
40
+
41
+ ---
42
+
43
+ ## πŸ“¦ Installation
44
+
45
+ ```bash
46
+ # 1. Core dependencies
47
+ pip install torch torchvision transformers pillow numpy
48
+
49
+ # 2. Install this wrapper
50
+ pip install -e .
51
+ ```
52
+
53
+ DepthPro is a large ViT-L model (~2 GB). The weights download automatically from HuggingFace the first time you instantiate `DepthProEstimator`.
54
+
55
+ > **GPU strongly recommended.** The model runs in ~0.3 s on a modern GPU; CPU inference is possible but extremely slow.
56
+
57
+ ---
58
+
59
+ ## πŸ”¬ How it works (the full pipeline)
60
+
61
+ Here is exactly what happens under the hood when you call `estimate()`.
62
+
63
+ ### Step 1 β€” Preprocessing (handled automatically)
64
+ Your input image is resized to **1536 Γ— 1536** (the fixed operating resolution DepthPro was trained on), rescaled by `1/255`, and normalised to `[-1, 1]` with `mean=0.5, std=0.5`. This is done by `DepthProImageProcessorFast`.
65
+
66
+ ### Step 2 β€” Feature extraction (DINOv2 ViT-L + multi-scale patches)
67
+ DepthPro's backbone is a **DINOv2 ViT-L/16** (24 layers, 1024 hidden dim, 16Γ—16 patch size). It processes the image at three scales simultaneously:
68
+
69
+ | Scale | Resolution | Purpose |
70
+ |-------|-----------|---------|
71
+ | 0.25Γ— | 384Γ—384 | Global context, far-away geometry |
72
+ | 0.5Γ— | 768Γ—768 | Mid-range structure |
73
+ | 1.0Γ— | 1536Γ—1536 | Fine detail, edges, thin structures |
74
+
75
+ The three-scale features are fused with a **DPT-style decoder** (hidden size 256) into a dense feature map.
76
+
77
+ ### Step 3 β€” Depth prediction (canonical inverse depth)
78
+ The decoder outputs **canonical inverse depth** `C`. This is not the final metric depth yet β€” it is a scale-invariant representation that the network learns to predict robustly across scenes. The actual metric depth is recovered in the post-processing step.
79
+
80
+ ### Step 4 β€” FOV / focal-length estimation (no calibration needed)
81
+ DepthPro has a **dedicated FOV head**. It ingests frozen features from the depth network plus task-specific features from a separate ViT encoder to predict the **horizontal field of view (FOV)** in degrees.
82
+
83
+ From the FOV, the focal length in pixels is derived:
84
+
85
+ ```
86
+ focal_length = (image_width / 2) / tan(FOV / 2)
87
+ ```
88
+
89
+ This is the critical piece that makes the depth **metric** (in meters) rather than just up-to-scale.
90
+
91
+ ### Step 5 β€” Post-processing (metric depth + back-projection)
92
+ The processor converts canonical inverse depth `C` to metric depth `D_m` using the estimated focal length:
93
+
94
+ ```
95
+ D_m = (focal_length Γ— image_width) / C
96
+ ```
97
+
98
+ Then, using the **pinhole camera model**, every pixel `(u, v)` is back-projected to a 3D point:
99
+
100
+ ```
101
+ X = (u - cx) * Z / focal_length
102
+ Y = (v - cy) * Z / focal_length
103
+ Z = D_m[v, u]
104
+ ```
105
+
106
+ where `(cx, cy)` is the principal point (image centre by default). DepthPro assumes **square pixels** (`fx == fy`), which is standard for most modern cameras.
107
+
108
+ ### Result
109
+ You get:
110
+ * `depth` β€” (H, W) metric depth map in **meters**
111
+ * `focal_length` β€” estimated focal length in **pixels**
112
+ * `field_of_view` β€” estimated horizontal FOV in **degrees**
113
+ * `points` β€” (N, 3) 3D point cloud in camera coordinates (`+Z` forward)
114
+
115
+ ---
116
+
117
+ ## 🧰 API Reference
118
+
119
+ ### `DepthProEstimator`
120
+
121
+ ```python
122
+ class DepthProEstimator(
123
+ model_name="apple/DepthPro-hf",
124
+ device="cuda:0",
125
+ dtype=torch.float16,
126
+ )
127
+ ```
128
+
129
+ * `model_name` β€” HuggingFace model ID or local path.
130
+ * `device` β€” PyTorch device. CUDA strongly recommended.
131
+ * `dtype` β€” `torch.float16` (default, fast) or `torch.float32` (slightly higher precision).
132
+
133
+ #### `.estimate(image)`
134
+
135
+ ```python
136
+ result = estimator.estimate(
137
+ image, # str, Path, PIL.Image, or np.ndarray
138
+ return_confidence=False,
139
+ )
140
+ ```
141
+
142
+ Returns a `DepthResult` dataclass:
143
+
144
+ | Attribute | Shape | Description |
145
+ |-----------|-------|-------------|
146
+ | `depth` | (H, W) | Metric depth in meters (float32) |
147
+ | `focal_length` | scalar | Estimated focal length in pixels |
148
+ | `field_of_view` | scalar | Estimated horizontal FOV in degrees |
149
+ | `image` | (H, W, 3) | Original RGB image (uint8) |
150
+ | `confidence` | (H, W) or None | Per-pixel confidence (if requested) |
151
+ | `height`, `width` | scalars | Convenience properties |
152
+
153
+ #### `.estimate_batch(images)`
154
+
155
+ Process multiple images in a single forward pass for efficiency:
156
+
157
+ ```python
158
+ results = estimator.estimate_batch(["a.jpg", "b.jpg", "c.jpg"])
159
+ for r in results:
160
+ print(r.depth.shape)
161
+ ```
162
+
163
+ ### `depth_to_point_cloud(depth, focal_length, ...)`
164
+
165
+ ```python
166
+ from depthpro_wrapper import depth_to_point_cloud
167
+
168
+ points = depth_to_point_cloud(
169
+ depth=result.depth, # (H, W) metric depth
170
+ focal_length=result.focal_length,
171
+ principal_point=None, # default = image centre
172
+ mask=None, # optional boolean mask
173
+ sample_step=1, # 2 = 1/4 points, 4 = 1/16
174
+ )
175
+ ```
176
+
177
+ Returns `(N, 3)` float32 array of 3D points in camera coordinates.
178
+
179
+ ### `rgbd_to_point_cloud(depth, rgb, focal_length, ...)`
180
+
181
+ Same as above but also returns per-point RGB colours:
182
+
183
+ ```python
184
+ points, colors = rgbd_to_point_cloud(
185
+ result.depth, result.image, result.focal_length,
186
+ sample_step=2,
187
+ )
188
+ ```
189
+
190
+ Returns `(N, 3)` points and `(N, 3)` uint8 colours.
191
+
192
+ ### `normals_from_depth(depth, focal_length)`
193
+
194
+ Compute surface normals directly from the depth map (useful for feeding into surface-reconstruction pipelines like Poisson or NKSR):
195
+
196
+ ```python
197
+ from depthpro_wrapper import normals_from_depth
198
+ normals = normals_from_depth(result.depth, result.focal_length)
199
+ ```
200
+
201
+ Returns `(H, W, 3)` float32 unit normals (unoriented).
202
+
203
+ ### `save_point_cloud(path, points, colors=None, normals=None)`
204
+
205
+ Save a point cloud to an ASCII PLY file (readable by Open3D, MeshLab, CloudCompare, Blender, etc.):
206
+
207
+ ```python
208
+ from depthpro_wrapper import save_point_cloud
209
+ save_point_cloud("cloud.ply", points, colors=colors, normals=normals)
210
+ ```
211
+
212
+ ---
213
+
214
+ ## πŸ–₯️ CLI Usage
215
+
216
+ ```bash
217
+ # Basic: image β†’ point cloud
218
+ python scripts/image_to_pointcloud.py photo.jpg cloud.ply
219
+
220
+ # With colours and normals
221
+ python scripts/image_to_pointcloud.py photo.jpg cloud.ply --colored --normals
222
+
223
+ # Down-sample for faster processing / smaller files
224
+ python scripts/image_to_pointcloud.py photo.jpg cloud.ply --sample-step 2
225
+
226
+ # Save intermediate depth & confidence maps
227
+ python scripts/image_to_pointcloud.py photo.jpg cloud.ply \
228
+ --save-depth depth.npy --save-confidence conf.npy
229
+
230
+ # CPU fallback (very slow)
231
+ python scripts/image_to_pointcloud.py photo.jpg cloud.ply --device cpu --dtype float32
232
+ ```
233
+
234
+ ---
235
+
236
+ ## πŸ“‚ Repository layout
237
+
238
+ ```
239
+ depthpro-wrapper/
240
+ β”œβ”€β”€ depthpro_wrapper/
241
+ β”‚ β”œβ”€β”€ __init__.py # public API
242
+ β”‚ β”œβ”€β”€ depth_estimator.py # DepthProEstimator + DepthResult
243
+ β”‚ β”œβ”€β”€ point_cloud.py # back-projection + normal estimation
244
+ β”‚ └── io.py # image / PLY I/O helpers
245
+ β”œβ”€β”€ scripts/
246
+ β”‚ └── image_to_pointcloud.py # CLI entry point
247
+ β”œβ”€β”€ examples/
248
+ β”‚ β”œβ”€β”€ quickstart.py # 10-line minimal example
249
+ β”‚ └── batch_processing.py # folder-of-images batch script
250
+ β”œβ”€β”€ setup.py
251
+ β”œβ”€β”€ requirements.txt
252
+ └── README.md
253
+ ```
254
+
255
+ ---
256
+
257
+ ## 🎯 Tips & Troubleshooting
258
+
259
+ | Problem | Solution |
260
+ |---------|----------|
261
+ | Out of memory | Use `dtype=torch.float16` (default). If still OOM, use `--sample-step 2` or smaller images. |
262
+ | Depth looks wrong / flat | DepthPro works best on images with perspective (indoor rooms, outdoor scenes). Very flat macro shots may under-estimate depth. |
263
+ | Point cloud is noisy at edges | Depth has uncertainty at object boundaries. Use `sample_step=2` or filter by `confidence` if you saved it. |
264
+ | Focal length seems off | DepthPro estimates FOV from image content. Very unusual aspect ratios or heavy cropping can confuse it. You can override with your own `focal_length` in `depth_to_point_cloud()`. |
265
+ | Want a mesh, not a point cloud | Feed the point cloud into a surface-reconstruction method: Poisson (Open3D), Alpha shapes, or better yet **[NKSR](https://huggingface.co/bdck/nksr-wrapper)** for neural surface reconstruction. |
266
+ | Batch processing is slow | Use `estimate_batch()` with batch size 4–8 instead of looping over `estimate()`. |
267
+
268
+ ---
269
+
270
+ ## πŸ”— Citation
271
+
272
+ If you use DepthPro in your research, please cite the original paper:
273
+
274
+ ```bibtex
275
+ @article{depthpro2024,
276
+ title={Depth Pro: Sharp Monocular Metric Depth in Less Than a Second},
277
+ author={von_PLaten et al.},
278
+ journal={arXiv preprint arXiv:2410.02073},
279
+ year={2024}
280
+ }
281
+ ```
282
+
283
+ Original code: [https://github.com/apple/ml-depth-pro](https://github.com/apple/ml-depth-pro)
284
+ HuggingFace model: [https://huggingface.co/apple/DepthPro-hf](https://huggingface.co/apple/DepthPro-hf)
285
+
286
+ ---
287
+
288
+ ## πŸ“„ License
289
+
290
+ This wrapper is released under the MIT License. DepthPro itself is under Apple's own license (see the original repository).
291
+
292
+ ---
293
+
294
+ *Built with ❀️ on top of Apple's DepthPro.*