Export Depth Point Cloud (PLY)
The node that turns your flat image into actual 3D points
- depth_map
- color_image
- ply_paths
- manifest_json
This is the node where the magic becomes math. You give it an image and a depth map, and it unprojects every pixel into a 3D point, colored by the RGB of the original image. One flat photo in, a real point cloud out - the thing you can fly a camera through, orbit, and reproject from new angles. Everything downstream in camera-comfyUI - camera motion, point-cloud inpainting, splat building - starts here.
It sits right after a depth estimator in every single cloud workflow in the pack. The PointCloud.json demo, the fisheye-to-pointcloud pipeline, the pointcloud_inpaint flow - all of them open with this node. If you've ever done the "living photo" parallax trick and wanted to push further than a subtle warp, this is the honest 3D version of it.
How it works
For each pixel, it takes the depth value and the pixel's position in the frame, then inverts the camera projection using your chosen input_projection (PINHOLE, FISHEYE, or EQUIRECTANGULAR) and input_horizontal_fov (default 90°) to get a ray direction; the depth tells it how far to travel along that ray. The result is a point cloud tensor of shape (N,7) - X, Y, Z plus R, G, B, A per point - the format the whole pack speaks.
The image is required because the node is built around coloring points; the depthmap is actually optional, fed in via the optional input slot. If you leave depth disconnected, the node still emits a cloud - it just can't place geometry correctly without a real depth source, so don't do that. When you do wire in depthmap, also wire the optional mask if you have one (e.g. from DepthEdgeFilter) so masked-out pixels don't become stray points.
Three settings get most of the attention:
input_projection- must match how your depth was produced. Fisheye depth from FisheyeDepthEstimator goes with FISHEYE, pinhole depth with PINHOLE. Get this wrong and the cloud folds in on itself.depth_scale(default 1.0) - a multiplier on depth values; effectively your scene's unit of measurement.invert_depth(default off) - flips the map if your estimator outputs "closer = darker."
The Z-depth trap (again)
The depth tensor you feed this node should be ray depth, not Z-depth. Depth Anything V2 outputs Z-depth, so the pack's README is emphatic: run the map through ZDepthToRayDepthNode before this node, or your point cloud bows outward at wide FOVs. At 90° it's a subtle curve; at 180° fisheye it's a wrecked scene. This is the single most common "why is my point cloud curved" issue in the pack.
Install
Shared camera-comfyUI install: ComfyUI Manager → search "camera-comfyUI" → Install, or clone https://github.com/Alexankharin/camera-comfyUI into custom_nodes/ and run python install.py. No models of its own - the depth comes from upstream - and it runs on CPU fine.
Troubleshooting
- Cloud is a folded mess:
input_projectiondoesn't match the depth source, or the depth is Z-depth at wide FOV. Check both. - Black point everywhere: the mask (or depth) is cutting out most pixels, or the image fed in has a lot of alpha=0. Check your mask's polarity.
- Point cloud is the right shape but wrong size:
depth_scale- divide or multiply to set units. - Flying pixels at object edges: pass the depth through DepthEdgeFilter and feed its
valid_maskinto themaskinput here.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| depth_map | IMAGE | — | |
| field_of_view | FLOAT | 601–179 | — |
| depth_scale | FLOAT | 1.000.001–1000 | — |
| stride | INT | 41–64 | — |
| drop_zero_depth | BOOLEAN | true | — |
| filename_prefix | STRING | depth_exports/point_cloud | — |
| color_imageopt | IMAGE | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| ply_paths | STRING | — |
| manifest_json | STRING | — |