Metadata-Version: 2.4
Name: open3d-four-view
Version: 0.0.3
Summary: Render PCD point clouds into a single Open3D four-view merge PNG.
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: numpy
Requires-Dist: Pillow
Requires-Dist: PyYAML
Requires-Dist: open3d
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"

# open3d-four-view

Render one PCD into a single 2x2 four-view PNG with Open3D. The package renders top, front, right, and oblique views in memory, then writes only the final merge image.

v0.0.3 keeps the v0.0.2 default rendering behavior and adds optional PCD field inspection, PCD-aware legends, custom tile labels, and field-based pseudo coloring.

## Agent Usage

Agents should prefer the CLI for workflow scripts and the Python API for in-process integrations. Always pass absolute paths when the caller's working directory is uncertain.

Default 3 orthographic views + 1 axonometric view:

```bash
open3d-four-view \
  --input_path /abs/path/input.pcd \
  --output_path /abs/path/output.png
```

Four Z-up quadrant axonometric views:

```bash
open3d-four-view \
  --input_path /abs/path/input.pcd \
  --output_path /abs/path/output.png \
  --view-layout quadrant-axonometric \
  --axonometric-elev 25
```

Batch render all `.pcd` files in a directory:

```bash
open3d-four-view \
  --input_dir /abs/path/pcds \
  --output_dir /abs/path/out \
  --view-layout standard
```

Recommended agent defaults:

- Use `--view-layout standard` when the report expects top/front/right/perspective.
- Use `--view-layout quadrant-axonometric` when the report needs four comparable Z-up oblique views from different XY quadrants.
- Use `--coverage 1.0` unless outliers make the point cloud too small in the image.
- Use `--gpu` only when GPU graphics rendering is desired; do not use it to request CUDA tensor point-cloud processing.
- Keep `--image-width`, `--image-height`, and `--max-points-plot` at defaults unless the downstream document requires a different resolution or rendering time is too high.

## Python API

```python
from open3d_four_view import render_pcd_merge_view

render_pcd_merge_view(
    "input.pcd",
    output_path="output.png",
    title="merge-view",
    overlay_info={
        "Viewpoint": "Merged four views",
        "car_model_id": "CAR001",
        "branch": "main",
        "commit": "abc123",
        "calib_pb_txt": "/tmp/vehicle_config.pb.txt",
        "soc": "soc2",
        "run_timestamp": "20260522-120000",
        "create_time": "2026-05-22 12:00:00",
    },
    color_legend={
        "at128p_front": {"rgb": [255, 0, 0]},
        "lidar_right": {"rgb": [0, 255, 0]},
    },
    view_layout="quadrant-axonometric",
    axonometric_elev=25.0,
    gpu=True,
)
```

Inspect PCD fields without relying on Open3D preserving non-geometry fields:

```python
from open3d_four_view import inspect_pcd_info, read_pcd_fields

fields = read_pcd_fields("input.pcd")
info = inspect_pcd_info("input.pcd")
```

`output_path` has the highest priority and is used exactly. Without `output_path`, the default single-file output is `<input_dir>/<base>_merge.png`. With `output_dir`, the output is `<output_dir>/<base>_merge.png`.

`gpu=True` requests GPU graphics rendering through Open3D `OffscreenRenderer` when available. It does not enable Open3D CUDA tensor point-cloud processing.

`view_layout` defaults to `"standard"`, which keeps the original top/front/right/perspective layout. Set it to `"quadrant-axonometric"` to render four Z-up axonometric views from XY quadrants at azimuth angles 45, 135, 225, and 315 degrees. `axonometric_elev` controls the elevation angle for that layout.

The 2x2 positions and labels are:

- `standard`: left-top `顶视图`, right-top `前视图`, left-bottom `右视图`, right-bottom `轴测图(方位{azim}°, 俯仰{elev}°)`.
- `quadrant-axonometric`: left-top `轴测图(第1象限, 方位45°, 俯仰{axonometric_elev}°)`, right-top 第2象限/135°, left-bottom 第3象限/225°, right-bottom 第4象限/315°.

### API Parameters

| Parameter | Type / Default | Meaning |
| --- | --- | --- |
| `input_path` | `str`, required | Input `.pcd` file path. |
| `output_path` | `str | None`, default `None` | Exact output PNG path for single-file rendering. Highest priority. |
| `output_dir` | `str | None`, default `None` | Output directory. Used as `<output_dir>/<pcd_stem>_merge.png` when `output_path` is not set. |
| `title` | `str`, default `"merge-view"` | Title text drawn at the top of the merged image. |
| `overlay_info` | `dict | None`, default `None` | Metadata drawn in the lower-left overlay. Supported keys include `Viewpoint`, `car_model_id`, `branch`, `commit`, `create_time`, and `soc`. |
| `color_legend` | `dict/list/None`, default `None` | Lower-right color legend. Each item should provide a sensor/name and RGB color. |
| `coverage` | `float`, default `1.0` | Point-cloud range used to fit the camera. Must be `0..1`; lower values crop outliers for framing only. |
| `image_width` | `int`, default `2400` | Width of each sub-view before 2x2 merge. Final PNG width is `image_width * 2`. |
| `image_height` | `int`, default `1800` | Height of each sub-view before 2x2 merge. Final PNG height is `image_height * 2`. |
| `max_points_plot` | `int`, default `500000` | Maximum points rendered. Larger clouds are downsampled by stride for performance. |
| `view_angles` | `dict | None`, default `None` | Optional `{elev, azim}` for the `standard` layout's single perspective view. |
| `view_layout` | `str`, default `"standard"` | `standard` or `quadrant-axonometric`. Controls the 2x2 view composition. |
| `axonometric_elev` | `float`, default `25.0` | Elevation angle for `quadrant-axonometric`; ignored by `standard`. |
| `gpu` | `bool`, default `False` | Request GPU graphics rendering through Open3D `OffscreenRenderer` when available. |
| `tile_labels` | `dict | None`, default `None` | Optional `default` and per-view `top`/`bottom_left`/`bottom_right` labels. |
| `legend_mode` | `str`, default `"static"` | `static`, `pcd`, or `auto`; `static` preserves old `color_legend` behavior. |
| `legend_pcd_path` | `str | None`, default `None` | PCD path used to detect actually present RGB colors for `pcd` legend mode. |
| `field_color` | `dict | None`, default `None` | Optional field pseudo-color config, e.g. `{"field":"intensity","input_range":[1,255],"output_range":[1,100]}`. |
| `right_panel_mode` | `str`, default `"auto"` | `auto`, `color_legend`, `field_range`, or `none`. |

## CLI

```bash
open3d-four-view --input_path input.pcd --output_path output.png
open3d-four-view --input_path input.pcd --output_path output.png --gpu
open3d-four-view --input_dir pcds --output_dir out --title merge-view
open3d-four-view --input_dir pcds --output_dir out --gpu
open3d-four-view --input_path input.pcd --output_path output.png --view-layout quadrant-axonometric --axonometric-elev 25
open3d-four-view-info --input_path input.pcd --json
```

Overlay and legend data can be passed as JSON:

```bash
open3d-four-view \
  --input_path input.pcd \
  --output_path output.png \
  --overlay-json '{"car_model_id":"CAR001","branch":"main","commit":"abc123","calib_pb_txt":"/tmp/vehicle_config.pb.txt","soc":"soc2","run_timestamp":"20260522-120000","create_time":"2026-05-22 12:00:00"}' \
  --color-legend-json '{"at128p_front":{"rgb":[255,0,0]}}'
```

Optional v0.0.3 overlays:

```bash
open3d-four-view \
  --input_path input.pcd \
  --output_path output.png \
  --tile-labels-json '{"default":{"bottom_left":"CAR001"},"front":{"top":"front custom"}}' \
  --legend-mode auto \
  --color-legend-json '{"at128p_front":{"rgb":[255,0,0]}}' \
  --field-color-json '{"field":"intensity","input_range":[1,255],"output_range":[1,100]}' \
  --right-panel-mode auto
```

`--coverage` defaults to `1.0`, meaning full point-cloud range. Values must be from `0` to `1`.

`--view-layout` defaults to `standard`, meaning three orthographic front/top/right views plus one oblique axonometric view. `--view-layout quadrant-axonometric` switches the 2x2 merge image to four Z-up axonometric views from the four XY quadrants. `--axonometric-elev` defaults to `25` degrees and only affects the quadrant axonometric layout.

### CLI Parameters

| Option | Required | Default | Meaning |
| --- | --- | --- | --- |
| `--input_path` | One of `--input_path` / `--input_dir` | `None` | Single input `.pcd` path. |
| `--input_dir` | One of `--input_path` / `--input_dir` | `None` | Batch input directory. All direct child `*.pcd` files are rendered in sorted order. |
| `--output_path` | No | `None` | Exact output PNG path. Only valid with `--input_path`. |
| `--output_dir` | Required with `--input_dir` | `None` | Output directory. In batch mode each file becomes `<stem>_merge.png`. |
| `--title` | No | `merge-view` | Top title text. |
| `--overlay-json` | No | `None` | JSON object for lower-left metadata overlay. |
| `--color-legend-json` | No | `None` | JSON object/list for lower-right color legend. |
| `--coverage` | No | `1.0` | Camera-fit coverage in `0..1`. Use smaller values only to reduce outlier impact. |
| `--image-width` | No | `2400` | Per-view width; final image is double this width. |
| `--image-height` | No | `1800` | Per-view height; final image is double this height. |
| `--max-points-plot` | No | `500000` | Maximum rendered point count after stride downsampling. |
| `--view-angles-yaml` | No | `None` | YAML dict such as `'{elev: 25, azim: 45}'`; affects only `standard` perspective view. |
| `--view-layout` | No | `standard` | `standard` keeps top/front/right/perspective; `quadrant-axonometric` uses four Z-up oblique quadrant views. |
| `--axonometric-elev` | No | `25.0` | Elevation angle for `quadrant-axonometric`; ignored by `standard`. |
| `--gpu` | No | `False` | Request GPU graphics rendering backend when available; warning + fallback if not confirmed. |
| `--tile-labels-json` | No | `None` | JSON object with `default` and per-view label overrides. |
| `--legend-mode` | No | `static` | `static`, `pcd`, or `auto`; `static` preserves old legend behavior. |
| `--legend-pcd-path` | No | `None` | PCD path used for RGB legend matching; defaults to input PCD when omitted. |
| `--field-color-json` | No | `None` | JSON object for field pseudo coloring. |
| `--right-panel-mode` | No | `auto` | `auto`, `color_legend`, `field_range`, or `none`. |

Input rules:

- Specify exactly one of `--input_path` or `--input_dir`.
- `--output_path` is valid only with `--input_path`.
- `--output_dir` is required with `--input_dir`.
- If neither `--output_path` nor `--output_dir` is given in single-file mode, the PNG is written beside the input as `<stem>_merge.png`.

View layouts:

- `standard`: left-top `top`/`顶视图`, right-top `front`/`前视图`, left-bottom `right`/`右视图`, right-bottom `perspective`/`轴测图(方位{azim}°, 俯仰{elev}°)`.
- `quadrant-axonometric`: left-top azimuth `45`/第1象限, right-top `135`/第2象限, left-bottom `225`/第3象限, right-bottom `315`/第4象限; all use Z-up and `--axonometric-elev`.

## Rendering Backend Notes

Four-view rendering uses Open3D `OffscreenRenderer` with OpenGL/EGL graphics rendering. The `--gpu` flag asks Open3D to prefer GPU graphics rendering where the runtime supports it. It is intentionally not a CUDA tensor pipeline and does not run CUDA voxel/downsample operations.

When `--gpu` is used, the package removes known software-rendering environment variables such as `LIBGL_ALWAYS_SOFTWARE=true`, `OPEN3D_CPU_RENDERING`, and `MESA_LOADER_DRIVER_OVERRIDE=llvmpipe/softpipe` before importing Open3D. It keeps `EGL_PLATFORM=surfaceless` because that can be valid for headless EGL GPU rendering. If GPU rendering cannot be confirmed, a `RuntimeWarning` is emitted and rendering continues with the Open3D backend available in the current environment.

In current validation environments, Open3D 0.19.0 with CUDA 12.1 can use GPU rendering. On RTX 50 / Blackwell GPUs, avoid Open3D CUDA tensor point-cloud batch processing unless you are using a CUDA/Open3D build that explicitly supports Blackwell.

References:

- Open3D Tensor: https://www.open3d.org/docs/latest/tutorial/core/tensor.html
- Open3D CPU/Software Rendering: https://www.open3d.org/docs/latest/tutorial/visualization/cpu_rendering.html
- NVIDIA Blackwell Compatibility Guide: https://docs.nvidia.com/cuda/blackwell-compatibility-guide/
