Skip to content

Commit bd942ea

Browse files
Merge pull request #20 from OlafenwaMoses/main
release - remove mesh generation
2 parents 14010c1 + ffad609 commit bd942ea

28 files changed

Lines changed: 77 additions & 352 deletions

docs/features/depth_estimation.md

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
**Category:** Lifting (2D → 3D)
44
**Experimental:** No
55

6-
Depth estimation predicts the per-pixel distance from the camera for every pixel in a 2D RGB image, producing a depth map and optionally unprojecting it into a 3D point cloud or surface mesh. vizion3d uses [Depth Anything V2](https://github.com/DepthAnything/Depth-Anything-V2) as its default backend.
6+
Depth estimation predicts the per-pixel distance from the camera for every pixel in a 2D RGB image, producing a depth map and optionally unprojecting it into a 3D point cloud. vizion3d uses [Depth Anything V2](https://github.com/DepthAnything/Depth-Anything-V2) as its default backend.
77

88
---
99

@@ -40,7 +40,6 @@ Set `VIZION3D_MODEL_CACHE` in your environment to change the default cache direc
4040
| `model_backend` | `str` | No | vizion3D release checkpoint URL | Model backend identifier. See [Model backends](#model-backends) above. |
4141
| `return_depth_image` | `bool` | No | `False` | If `True`, the result includes a 16-bit grayscale Open3D Image of the depth map. |
4242
| `return_point_cloud` | `bool` | No | `False` | If `True`, the result includes an Open3D PointCloud unprojected from the RGB-D image. |
43-
| `return_mesh` | `bool` | No | `False` | If `True`, the result includes an Open3D TriangleMesh reconstructed from the point cloud via ball-pivoting. |
4443
| `advanced_config` | `DepthEstimationAdvanceConfig` | No | PrimeSense defaults | Camera intrinsics and depth range settings. See [Advanced config](#10-advanced-config-camera-intrinsics-depth-range) below. |
4544

4645
---
@@ -57,7 +56,6 @@ Set `VIZION3D_MODEL_CACHE` in your environment to change the default cache direc
5756
| `backend_used` | `str` | Yes | Resolved model identifier that processed the request (local file path). |
5857
| `depth_image` | `open3d.geometry.Image \| None` | When `return_depth_image=True` | 16-bit grayscale image, dtype `uint16`, shape `(H, W)`. The full 0–65535 range maps to `[min_depth, max_depth]`. |
5958
| `point_cloud` | `open3d.geometry.PointCloud \| None` | When `return_point_cloud=True` | Coloured 3D point cloud unprojected from the RGB-D image using the intrinsics in `advanced_config`. Coordinates are in metres. |
60-
| `mesh` | `open3d.geometry.TriangleMesh \| None` | When `return_mesh=True` | Triangle mesh surface reconstructed from the point cloud via ball-pivoting. Includes vertex colours. |
6159
| `point_cloud_scale` | `float` | Yes | Scale factor: multiply any distance measured between two points in the point cloud by this value to get the equivalent distance in metres. Always `1.0` — Open3D produces point cloud coordinates directly in metres. |
6260

6361
---
@@ -154,33 +152,9 @@ o3d.io.write_point_cloud("scene.ply", pcd)
154152

155153
---
156154

157-
## 5. Surface mesh
155+
## 5. All outputs at once
158156

159-
Request a triangulated mesh reconstructed from the point cloud via ball-pivoting. Includes vertex colours.
160-
161-
```python
162-
import open3d as o3d
163-
from vizion3d.lifting import DepthEstimation, DepthEstimationCommand
164-
165-
cmd = DepthEstimationCommand(
166-
image_input="scene.png",
167-
return_mesh=True,
168-
)
169-
result = DepthEstimation().run(cmd)
170-
171-
mesh = result.mesh # open3d.geometry.TriangleMesh
172-
print(f"Vertices : {len(mesh.vertices)}")
173-
print(f"Triangles : {len(mesh.triangles)}")
174-
175-
# Save as PLY
176-
o3d.io.write_triangle_mesh("scene_mesh.ply", mesh)
177-
```
178-
179-
---
180-
181-
## 6. All outputs at once
182-
183-
All three optional outputs can be requested in a single inference pass.
157+
Both optional outputs can be requested in a single inference pass.
184158

185159
```python
186160
import numpy as np
@@ -191,7 +165,6 @@ cmd = DepthEstimationCommand(
191165
image_input="scene.png",
192166
return_depth_image=True,
193167
return_point_cloud=True,
194-
return_mesh=True,
195168
)
196169
result = DepthEstimation().run(cmd)
197170

@@ -204,14 +177,11 @@ depth_arr = np.asarray(result.depth_image) # uint16 (H, W)
204177
# Point cloud
205178
pcd = result.point_cloud
206179
o3d.io.write_point_cloud("scene.ply", pcd)
207-
208-
# Mesh
209-
o3d.io.write_triangle_mesh("scene_mesh.ply", result.mesh)
210180
```
211181

212182
---
213183

214-
## 7. Custom model backend
184+
## 6. Custom model backend
215185

216186
Use a local `.pth` checkpoint or a remote URL to a `.pth` file.
217187

@@ -240,7 +210,7 @@ print(f"Backend: {result.backend_used}")
240210

241211
---
242212

243-
## 8. REST API
213+
## 7. REST API
244214

245215
Start the server with all REST features enabled:
246216

@@ -284,15 +254,14 @@ Send a request with `multipart/form-data`:
284254
```bash
285255
curl -X POST "http://localhost:8000/lifting/depth-estimation" \
286256
-F "image=@scene.png" \
287-
-F "return_point_cloud=true" \
288-
-F "return_mesh=true"
257+
-F "return_point_cloud=true"
289258
```
290259

291-
The response is a JSON-serialised `DepthEstimationResult`. Binary fields (`depth_image`, `point_cloud`, `mesh`) are base64-encoded in the JSON response.
260+
The response is a JSON-serialised `DepthEstimationResult`. Binary fields (`depth_image`, `point_cloud_ply`) are base64-encoded in the JSON response.
292261

293262
---
294263

295-
## 9. gRPC API
264+
## 8. gRPC API
296265

297266
Start the server:
298267

@@ -321,7 +290,6 @@ with open("scene.png", "rb") as f:
321290
request = lifting_pb2.DepthEstimationRequest(
322291
image_bytes=img_bytes,
323292
return_point_cloud=True,
324-
return_mesh=True,
325293
)
326294

327295
response = stub.RunDepthEstimation(request)
@@ -332,7 +300,7 @@ print(f"Backend : {response.backend_used}")
332300

333301
---
334302

335-
## 10. Advanced config: camera intrinsics & depth range
303+
## 9. Advanced config: camera intrinsics & depth range
336304

337305
`DepthEstimationAdvanceConfig` lets you supply the actual camera intrinsics and depth range for your sensor, replacing the built-in PrimeSense defaults. This is required for accurate metric 3D geometry when your camera is not a 640×480 PrimeSense sensor.
338306

@@ -362,8 +330,9 @@ The same config is available in the REST and gRPC entry points. See [Advanced Co
362330

363331
---
364332

333+
---
334+
365335
## Known limitations
366336

367337
- **Relative depth only** — the default monocular backend produces relative (inverse) depth, not metric depth. Point cloud distances are internally consistent but not calibrated to real-world scale without a known reference distance.
368-
- **Ball-pivoting mesh quality** — the mesh reconstructor works best on dense, evenly sampled point clouds. Sparse or noisy clouds may produce gaps or missing faces.
369-
- **Python 3.12 required for Open3D**`return_depth_image`, `return_point_cloud`, and `return_mesh` require Open3D, which currently only supports Python 3.12 in this project.
338+
- **Python 3.12 required for Open3D**`return_depth_image` and `return_point_cloud` require Open3D, which currently only supports Python 3.12 in this project.

docs/features/stereo_depth.md

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ The S2M2 architecture comes in four size variants. The correct one is detected
5656
| `model_backend` | `str` | No | vizion3D release checkpoint URL | S2M2 checkpoint. See [Model backends](#model-backends) above. |
5757
| `return_depth_image` | `bool` | No | `False` | If `True`, the result includes a 16-bit grayscale Open3D Image of the depth map. |
5858
| `return_point_cloud` | `bool` | No | `False` | If `True`, the result includes an Open3D PointCloud in metres. |
59-
| `return_mesh` | `bool` | No | `False` | If `True`, the result includes an Open3D TriangleMesh reconstructed via ball-pivoting. |
6059
| `advanced_config` | `StereoDepthAdvancedConfig` | No | 1280×720 @ 100 mm baseline defaults | Camera intrinsics and inference settings. See [Advanced config](#advanced-config) below. |
6160

6261
---
@@ -74,7 +73,6 @@ The S2M2 architecture comes in four size variants. The correct one is detected
7473
| `backend_used` | `str` | Yes | Resolved local file path of the checkpoint used. |
7574
| `depth_image` | `open3d.geometry.Image \| None` | When `return_depth_image=True` | 16-bit grayscale image, dtype `uint16`. The full 0–65535 range maps to `[min_depth, max_depth]` in metres. |
7675
| `point_cloud` | `open3d.geometry.PointCloud \| None` | When `return_point_cloud=True` | Coloured 3D point cloud, coordinates in **metres**. |
77-
| `mesh` | `open3d.geometry.TriangleMesh \| None` | When `return_mesh=True` | Surface mesh from ball-pivoting. Includes vertex colours. |
7876
| `point_cloud_scale` | `float` | Yes | Always `1.0` — stereo depth produces real metric coordinates. |
7977

8078
---
@@ -187,28 +185,7 @@ o3d.io.write_point_cloud("scene.ply", pcd)
187185

188186
---
189187

190-
## 6. Surface mesh
191-
192-
```python
193-
import open3d as o3d
194-
from vizion3d.stereo import StereoDepth, StereoDepthCommand
195-
196-
cmd = StereoDepthCommand(
197-
left_image="left.png",
198-
right_image="right.png",
199-
return_mesh=True,
200-
)
201-
result = StereoDepth().run(cmd)
202-
203-
mesh = result.mesh
204-
print(f"Vertices : {len(mesh.vertices)}")
205-
print(f"Triangles : {len(mesh.triangles)}")
206-
o3d.io.write_triangle_mesh("scene_mesh.ply", mesh)
207-
```
208-
209-
---
210-
211-
## 7. All outputs at once
188+
## 6. All outputs at once
212189

213190
```python
214191
import numpy as np
@@ -220,19 +197,17 @@ cmd = StereoDepthCommand(
220197
right_image="right.png",
221198
return_depth_image=True,
222199
return_point_cloud=True,
223-
return_mesh=True,
224200
)
225201
result = StereoDepth().run(cmd)
226202

227203
print(f"Depth range : {result.min_depth:.2f}{result.max_depth:.2f} m")
228204
depth_arr = np.asarray(result.depth_image) # uint16 (H, W)
229205
o3d.io.write_point_cloud("scene.ply", result.point_cloud)
230-
o3d.io.write_triangle_mesh("scene_mesh.ply", result.mesh)
231206
```
232207

233208
---
234209

235-
## 8. Speed vs quality: scale factor
210+
## 7. Speed vs quality: scale factor
236211

237212
Use `scale_factor < 1.0` to downsample input before inference for faster results:
238213

@@ -251,7 +226,7 @@ result = StereoDepth().run(cmd)
251226

252227
---
253228

254-
## 9. REST API
229+
## 8. REST API
255230

256231
Start the server with all REST features enabled:
257232

@@ -305,11 +280,11 @@ curl -X POST "http://localhost:8000/lifting/stereo-depth" \
305280
-F "return_point_cloud=true"
306281
```
307282

308-
The response is a JSON-serialised `StereoDepthResult`. Binary fields (`depth_image`, `point_cloud_ply`, `mesh_ply`) are base64-encoded.
283+
The response is a JSON-serialised `StereoDepthResult`. Binary fields (`depth_image`, `point_cloud_ply`) are base64-encoded.
309284

310285
---
311286

312-
## 10. gRPC API
287+
## 9. gRPC API
313288

314289
Start the server:
315290

@@ -422,5 +397,4 @@ cfg = StereoDepthAdvancedConfig(
422397

423398
- **Rectified pairs required** — images must be stereo-rectified so corresponding points lie on the same horizontal scanline. Un-rectified pairs will produce incorrect results.
424399
- **Metric scale depends on calibration** — an incorrect `baseline` or `focal_length` scales all depth values uniformly. Always use calibrated values for real applications.
425-
- **Ball-pivoting mesh quality** — works best on dense, evenly sampled point clouds. Sparse or noisy clouds from occluded regions may produce gaps or missing faces.
426-
- **Python 3.12 required for Open3D**`return_depth_image`, `return_point_cloud`, and `return_mesh` require Open3D, which currently only supports Python 3.12 in this project.
400+
- **Python 3.12 required for Open3D**`return_depth_image` and `return_point_cloud` require Open3D, which currently only supports Python 3.12 in this project.

docs/index.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ For per-backend prerequisites, install commands, and platform notes, see the [Ha
4646

4747
## Quick start — depth estimation
4848

49-
Get a depth map, point cloud, and mesh from a single image in under 10 lines.
49+
Get a depth map and point cloud from a single image in under 10 lines.
5050

5151
```python
5252
import open3d as o3d
@@ -56,7 +56,6 @@ result = DepthEstimation().run(
5656
DepthEstimationCommand(
5757
image_input="scene.png",
5858
return_point_cloud=True,
59-
return_mesh=True,
6059
)
6160
)
6261

@@ -65,7 +64,6 @@ print(f"Points : {len(result.point_cloud.points)}")
6564
print(f"Scale : {result.point_cloud_scale} metre per unit")
6665

6766
o3d.io.write_point_cloud("scene.ply", result.point_cloud)
68-
o3d.io.write_triangle_mesh("scene_mesh.ply", result.mesh)
6967
```
7068

7169
---

tests/assets/indoor_scene.jpg

711 KB
Loading

tests/integration/test_stereo_depth_direct.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def test_stereo_direct_advanced_config_scale_factor(
232232
def test_stereo_direct_all_outputs_returned(
233233
stereo_image_pair, stereo_advanced_config, local_stereo_model_path
234234
):
235-
"""depth_image, point_cloud, and mesh are all returned when requested."""
235+
"""depth_image and point_cloud are returned when requested."""
236236
StereoDepthHandler._stereo_models.clear()
237237
left_bytes, right_bytes = stereo_image_pair
238238

@@ -243,7 +243,6 @@ def test_stereo_direct_all_outputs_returned(
243243
model_backend=local_stereo_model_path,
244244
return_depth_image=True,
245245
return_point_cloud=True,
246-
return_mesh=True,
247246
advanced_config=stereo_advanced_config,
248247
)
249248
)
@@ -254,5 +253,3 @@ def test_stereo_direct_all_outputs_returned(
254253
assert result.point_cloud is not None
255254
assert result.point_cloud.has_points()
256255
assert result.point_cloud_scale == 1.0
257-
258-
assert result.mesh is not None

tests/unit/test_depth_estimation_advanced_config.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ def _post(self, extra_data=None):
242242
result.backend_used = "/fake/model.pth"
243243
result.depth_image = None
244244
result.point_cloud = None
245-
result.mesh = None
246245

247246
with patch("vizion3d.server.rest.depth_estimation.DepthEstimation") as mock_cls:
248247
mock_cls.return_value.run.return_value = result
@@ -322,7 +321,6 @@ def _run(self, proto_cfg=None):
322321
result.backend_used = "/fake/model.pth"
323322
result.depth_image = None
324323
result.point_cloud = None
325-
result.mesh = None
326324

327325
with patch("vizion3d.server.grpc.server.DepthEstimation") as mock_cls:
328326
mock_cls.return_value.run.return_value = result

tests/unit/test_depth_estimation_facade.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ def test_depth_estimation_basic(dummy_image_bytes):
2121
assert res.backend_used.endswith(DEFAULT_DEPTH_MODEL_FILENAME)
2222
assert res.depth_image is None
2323
assert res.point_cloud is None
24-
assert res.mesh is None
2524
assert res.point_cloud_scale == 1.0
2625

2726

@@ -92,20 +91,6 @@ def test_point_cloud_orientation_keeps_image_top_up():
9291
assert np.all(points[:, 2] > 0)
9392

9493

95-
def test_depth_estimation_returns_mesh(dummy_image_bytes):
96-
res = DepthEstimation().run(
97-
DepthEstimationCommand(image_input=dummy_image_bytes, return_mesh=True)
98-
)
99-
100-
assert isinstance(res.mesh, o3d.geometry.TriangleMesh)
101-
assert res.mesh.has_vertices()
102-
assert res.mesh.has_triangles()
103-
assert res.mesh.has_vertex_colors()
104-
import numpy as np
105-
106-
assert len(np.asarray(res.mesh.triangles)) > 0
107-
108-
10994
def test_depth_estimation_accepts_file_path(tmp_path):
11095
img = Image.new("RGB", (50, 50), color="red")
11196
img_path = tmp_path / "test.png"

tests/unit/test_depth_estimation_grpc.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ def _fake_result(depth_map=None):
3737
result.backend_used = "/fake/model.pth"
3838
result.depth_image = None
3939
result.point_cloud = None
40-
result.mesh = None
4140
return result
4241

4342

@@ -84,7 +83,6 @@ def test_grpc_optional_fields_empty_when_not_requested(servicer, mock_context, i
8483
response = servicer.RunDepthEstimation(request, mock_context)
8584
assert response.depth_image == b""
8685
assert response.point_cloud_ply == b""
87-
assert response.mesh_ply == b""
8886

8987

9088
def test_grpc_uses_default_backend_when_model_backend_is_empty(servicer, mock_context, image_bytes):
@@ -112,12 +110,10 @@ def test_grpc_forwards_return_flags(servicer, mock_context, image_bytes):
112110
image_bytes=image_bytes,
113111
return_depth_image=True,
114112
return_point_cloud=True,
115-
return_mesh=True,
116113
)
117114
with patch("vizion3d.server.grpc.server.DepthEstimation") as mock_cls:
118115
mock_cls.return_value.run.return_value = _fake_result()
119116
servicer.RunDepthEstimation(request, mock_context)
120117
called_cmd = mock_cls.return_value.run.call_args[0][0]
121118
assert called_cmd.return_depth_image is True
122119
assert called_cmd.return_point_cloud is True
123-
assert called_cmd.return_mesh is True

tests/unit/test_depth_estimation_stereo_depth_rest_api.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def _fake_result(depth_map=None):
4343
result.backend_used = "/fake/model.pth"
4444
result.depth_image = None
4545
result.point_cloud = None
46-
result.mesh = None
4746
return result
4847

4948

@@ -116,7 +115,6 @@ def test_depth_estimation_optional_outputs_null_by_default(image_file):
116115
data = response.json()
117116
assert data["depth_image"] is None
118117
assert data["point_cloud_ply"] is None
119-
assert data["mesh_ply"] is None
120118

121119

122120
def test_depth_estimation_backend_used_is_returned(image_file):

0 commit comments

Comments
 (0)