From a9e20b77646fd8f69d19e9efeca3a42f24bfc862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6kay=20Aydo=C4=9Fan?= <88277926+gokayfem@users.noreply.github.com> Date: Sat, 1 Aug 2026 09:55:09 +0300 Subject: [PATCH] Depth Toolkit v3: conditioning, geometry, parallax, and live 3D (#14) * feat: transform depth nodes into a complete v3 depth toolkit * fix(ci): validate metadata on Python 3.10 --- .comfyignore | 5 + .github/workflows/ci.yml | 64 ++- .github/workflows/publish.yml | 43 ++ .github/workflows/publish_action.yml | 114 ---- CHANGELOG.md | 9 + CITATION.cff | 6 +- README.md | 86 +-- SECURITY.md | 7 + __init__.py | 38 +- depth_nodes.py | 538 ++++++++++++++++++ docs/assets/live-comfyui.png | Bin 0 -> 101267 bytes examples/api/depth_toolkit_api.json | 19 + examples/workflows/Depth-Toolkit-Live.json | 22 + pyproject.toml | 4 +- tests/test_frontend_assets.py | 24 +- tests/test_toolkit.py | 84 +++ web/html/threeVisualizer.html | 15 +- web/js/threeVisualizer.mjs | 38 +- web/style/threeStyle.css | 6 + web/vendor/three.core.min.mjs | 6 + web/vendor/three.module.min.mjs | 2 +- ...sualization.js => viewer_extension_3_0.js} | 105 +++- 22 files changed, 1042 insertions(+), 193 deletions(-) create mode 100644 .comfyignore create mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/publish_action.yml create mode 100644 CHANGELOG.md create mode 100644 SECURITY.md create mode 100644 depth_nodes.py create mode 100644 docs/assets/live-comfyui.png create mode 100644 examples/api/depth_toolkit_api.json create mode 100644 examples/workflows/Depth-Toolkit-Live.json create mode 100644 tests/test_toolkit.py create mode 100644 web/vendor/three.core.min.mjs rename web/{visualization.js => viewer_extension_3_0.js} (56%) diff --git a/.comfyignore b/.comfyignore new file mode 100644 index 0000000..c167695 --- /dev/null +++ b/.comfyignore @@ -0,0 +1,5 @@ +.github/ +tests/ +__pycache__/ +.pytest_cache/ +*.py[cod] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e2fd8fa..fe28dbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,27 +3,69 @@ name: CI on: pull_request: push: - branches: - - main + branches: [main] permissions: contents: read jobs: - test: - runs-on: ubuntu-latest + python: + name: Python ${{ matrix.python }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 20 strategy: fail-fast: false matrix: - python-version: ["3.10", "3.12", "3.14"] + include: + - os: ubuntu-latest + python: "3.10" + - os: ubuntu-latest + python: "3.13" + - os: windows-latest + python: "3.12" + - os: macos-latest + python: "3.12" steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python }} cache: pip - - run: python -m pip install -r requirements.txt pytest - - run: python -m compileall -q . - - run: pytest -q - - run: node --input-type=module --check < web/visualization.js - - run: node --check web/js/threeVisualizer.mjs + - name: Install CPU PyTorch + if: runner.os != 'macOS' + run: python -m pip install --disable-pip-version-check --index-url https://download.pytorch.org/whl/cpu torch + - name: Install CPU PyTorch on macOS + if: runner.os == 'macOS' + run: python -m pip install --disable-pip-version-check torch + - name: Install test dependencies + run: python -m pip install --disable-pip-version-check -r requirements.txt pytest tomli + - name: Compile Python + run: python -m compileall -q . + - name: Test + run: pytest -q + - name: Validate Registry metadata + run: python -c "import pathlib,tomli; d=tomli.loads(pathlib.Path('pyproject.toml').read_text('utf-8')); assert d['project']['version']=='3.0.0'; assert d['tool']['comfy']['PublisherId']=='gokayfem'" + + frontend: + name: Frontend and examples + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v6 + - name: Check browser modules + run: | + node --input-type=module --check < web/viewer_extension_3_0.js + node --check web/js/threeVisualizer.mjs + node --check web/vendor/three.module.min.mjs + node --check web/vendor/three.core.min.mjs + - name: Reject runtime CDN imports + run: | + if grep -R -n -E 'https?://|@latest' web --include='*.html' --include='*.js' --include='*.mjs' --exclude-dir=vendor; then + echo 'Runtime remote dependency found.' >&2 + exit 1 + fi + - name: Validate example JSON + run: | + for file in examples/workflows/*.json examples/api/*.json; do + python -m json.tool "$file" >/dev/null + done diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..ecb1308 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,43 @@ +name: Publish to Comfy Registry + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - "pyproject.toml" + - ".github/workflows/publish.yml" + +concurrency: + group: comfy-registry-${{ github.repository }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + publish-node: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v6 + - name: Check Registry credential + id: credential + env: + REGISTRY_ACCESS_TOKEN: ${{ secrets.REGISTRY_ACCESS_TOKEN }} + shell: bash + run: | + if [[ -n "$REGISTRY_ACCESS_TOKEN" ]]; then + echo "available=true" >> "$GITHUB_OUTPUT" + else + echo "available=false" >> "$GITHUB_OUTPUT" + echo "::warning title=Registry publish skipped::Add the REGISTRY_ACCESS_TOKEN repository secret to enable publishing." + fi + - name: Publish Custom Node + if: steps.credential.outputs.available == 'true' + uses: Comfy-Org/publish-node-action@main + with: + personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} + - name: Record skipped publication + if: steps.credential.outputs.available != 'true' + run: echo "Registry publication was skipped because REGISTRY_ACCESS_TOKEN is not configured." >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/publish_action.yml b/.github/workflows/publish_action.yml deleted file mode 100644 index cf720e6..0000000 --- a/.github/workflows/publish_action.yml +++ /dev/null @@ -1,114 +0,0 @@ -name: Publish to Comfy registry -on: - workflow_dispatch: - push: - branches: - - main - paths: - - "pyproject.toml" - - ".github/workflows/publish_action.yml" - -concurrency: - group: comfy-registry-${{ github.repository }} - cancel-in-progress: false - -env: - COMFY_CLI_VERSION: "1.13.0" - -jobs: - publish-node: - name: Publish Custom Node to registry - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - steps: - - name: Check out code - uses: actions/checkout@v7 - - name: Set up Python - uses: actions/setup-python@v7 - with: - python-version: "3.12" - - name: Read release metadata - id: metadata - run: | - python - <<'PY' - import os - import tomllib - from pathlib import Path - - metadata = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8")) - node_id = metadata["project"]["name"] - version = metadata["project"]["version"] - publisher = metadata["tool"]["comfy"]["PublisherId"] - if publisher != "gokayfem": - raise SystemExit(f"Expected publisher 'gokayfem', found {publisher!r}") - - with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output: - print(f"node_id={node_id}", file=output) - print(f"version={version}", file=output) - PY - - name: Check Registry version - id: registry - env: - NODE_ID: ${{ steps.metadata.outputs.node_id }} - VERSION: ${{ steps.metadata.outputs.version }} - run: | - python - <<'PY' - import json - import os - import urllib.parse - import urllib.request - from pathlib import Path - - node_id = urllib.parse.quote(os.environ["NODE_ID"], safe="") - request = urllib.request.Request( - f"https://api.comfy.org/nodes/{node_id}/versions", - headers={"Accept": "application/json", "User-Agent": "comfy-node-publisher"}, - ) - with urllib.request.urlopen(request, timeout=30) as response: - versions = json.load(response) - - exists = any(item.get("version") == os.environ["VERSION"] for item in versions) - with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as output: - print(f"exists={'true' if exists else 'false'}", file=output) - PY - - name: Check publisher credential - if: steps.registry.outputs.exists != 'true' - id: credentials - env: - REGISTRY_ACCESS_TOKEN: ${{ secrets.REGISTRY_ACCESS_TOKEN }} - run: | - if [[ -n "$REGISTRY_ACCESS_TOKEN" ]]; then - echo "available=true" >> "$GITHUB_OUTPUT" - else - echo "available=false" >> "$GITHUB_OUTPUT" - echo "::notice title=Central publisher enabled::The secure fleet publisher will publish this release within one hour." - fi - - name: Install pinned Comfy CLI - if: steps.registry.outputs.exists != 'true' && steps.credentials.outputs.available == 'true' - shell: bash - run: python -m pip install --disable-pip-version-check "comfy-cli==${COMFY_CLI_VERSION}" - - name: Publish Custom Node - if: steps.registry.outputs.exists != 'true' && steps.credentials.outputs.available == 'true' - id: publish - continue-on-error: true - shell: bash - env: - REGISTRY_ACCESS_TOKEN: ${{ secrets.REGISTRY_ACCESS_TOKEN }} - run: comfy --skip-prompt --no-enable-telemetry node publish --token "$REGISTRY_ACCESS_TOKEN" - - name: Record publication result - env: - NODE_ID: ${{ steps.metadata.outputs.node_id }} - VERSION: ${{ steps.metadata.outputs.version }} - ALREADY_PUBLISHED: ${{ steps.registry.outputs.exists }} - PUBLISH_OUTCOME: ${{ steps.publish.outcome }} - run: | - if [[ "$ALREADY_PUBLISHED" == "true" ]]; then - echo "### $NODE_ID $VERSION already published" >> "$GITHUB_STEP_SUMMARY" - elif [[ "$PUBLISH_OUTCOME" == "success" ]]; then - echo "### Published $NODE_ID $VERSION" >> "$GITHUB_STEP_SUMMARY" - else - echo "::notice title=Central publishing handoff::The secure fleet publisher will retry this release within one hour." - echo "### $NODE_ID $VERSION queued for the fleet publisher" >> "$GITHUB_STEP_SUMMARY" - fi diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..75deaf2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## 3.0.0 + +- Added normalization, colormap, surface-normal, range-mask, cleanup, and statistics nodes. +- Added binary PLY point-cloud export and a zero-dependency glTF 2.0 GLB depth-mesh writer. +- Added video-ready parallax frame and validity-mask generation. +- Upgraded the live viewer with adaptive mesh quality, wireframe, 16-bit depth previews, passthrough outputs, cached output restoration, iframe reconnects, offscreen rendering, and WebGL recovery. +- Completed the pinned Three.js r185 module set and added real ComfyUI workflow/API proof, cross-platform CI, and official Registry publishing. diff --git a/CITATION.cff b/CITATION.cff index 8dc768f..6f19e7b 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,13 +2,13 @@ cff-version: 1.2.0 message: "If you use ComfyUI Depth Visualization in your work, please cite it using the metadata below." type: software title: "ComfyUI Depth Visualization" -version: "2.0.0" -date-released: 2026-07-28 +version: "3.0.0" +date-released: 2026-08-01 authors: - family-names: "Aydoğan" given-names: "Gökay" orcid: "https://orcid.org/0000-0002-2343-9433" -abstract: "An interactive, batch-aware depth-map visualization and mesh-export extension for ComfyUI." +abstract: "An offline depth conditioning, diagnostics, mask, normal, geometry export, parallax, and interactive 3D visualization toolkit for ComfyUI." keywords: - ComfyUI - depth map diff --git a/README.md b/README.md index 36dcf8a..75b6260 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,36 @@ # ComfyUI Depth Visualization -An interactive, in-node 3D preview for any ComfyUI reference image and depth -map. The current implementation is compatible with the modern ComfyUI -frontend, works offline, supports batches, and cleans up its WebGL resources -when a node is removed. +A complete offline depth-conditioning, diagnostics, 3D export, and parallax toolkit for ComfyUI. It cleans and normalizes arbitrary depth maps, creates masks, normals and colormaps, measures quality, exports real geometry, generates motion frames, and previews depth interactively in the graph. -![Depth Visualization](https://github.com/gokayfem/ComfyUI-Depth-Visualization/assets/88277926/0b63c2ed-60d4-44a6-9d44-b3548ec58d48) +![Executed Depth Toolkit workflow in ComfyUI](docs/assets/live-comfyui.png) -## Features +## Nodes -- Interactive orbit, pan, and zoom controls -- Adjustable positive or negative displacement -- Batch frame selector with single-image broadcasting -- PNG screenshots -- Baked depth-mesh export as GLB, GLTF, or OBJ -- Local, pinned Three.js assets with no runtime CDN dependency -- Correct copy/paste, collapse, resize, and node-removal behavior -- Stale-load cancellation and visible error reporting +| Node | Purpose | Outputs | +| --- | --- | --- | +| **Depth Viewer Pro** | GPU-accelerated displaced-mesh preview with adaptive quality | image/depth passthrough + live UI | +| **Normalize Depth** | Percentile, min-max, or fixed-range normalization with gamma/invert | normalized depth + JSON report | +| **Clean & Repair Depth** | Fill invalid holes and reduce noise while preserving edges | clean depth, repair MASK, report | +| **Colorize Depth** | Viridis, magma, turbo, plasma, or grayscale visualization | IMAGE | +| **Depth to Surface Normal** | Camera-aware normal generation with OpenGL/DirectX convention | normal map | +| **Depth Range Masks** | Feathered near/far selection | inside MASK, outside MASK, masked depth | +| **Analyze Depth** | Per-batch statistics and histogram rendering | JSON + histogram IMAGE | +| **Depth to Point Cloud** | Binary PLY export with optional colors | file paths + manifest | +| **Depth to Mesh** | Zero-dependency binary glTF 2.0 GLB export with optional colors | file paths + manifest | +| **Depth Parallax Frames** | Horizontal, vertical, ellipse, or dolly camera motion | IMAGE batch, validity MASK batch, manifest | -## Installation +## Viewer highlights -Install with ComfyUI Manager, or clone the repository manually: +- Orbit, pan, zoom, positive/negative displacement, reset, and wireframe +- Fast, balanced, and high adaptive mesh density +- Batch selection with single-image broadcasting +- PNG capture and baked GLB, GLTF, or OBJ export +- Lazy/offscreen rendering, bounded pixel ratio, stale-load cancellation, cleanup, and WebGL context recovery +- Local pinned Three.js r185 assets; no runtime CDN or telemetry + +## Install + +Install with ComfyUI Manager, or clone manually: ```bash cd ComfyUI/custom_nodes @@ -28,43 +38,53 @@ git clone https://github.com/gokayfem/ComfyUI-Depth-Visualization.git python -m pip install -r ComfyUI-Depth-Visualization/requirements.txt ``` -Restart ComfyUI after installation. +Restart ComfyUI. Nodes are under `visualization/3D` and `depth/toolkit`. -## Usage +## Start with the live example -1. Add **Depth Viewer** from `visualization/3D`. -2. Connect a reference `IMAGE` and a depth-map `IMAGE`. -3. Queue the workflow. -4. Drag to orbit, scroll to zoom, or right-drag to pan. +Load [`examples/workflows/Depth-Toolkit-Live.json`](examples/workflows/Depth-Toolkit-Live.json), select a reference image and depth map, and queue it. The bundled demonstration uses image luminance as stand-in depth so it runs without a model; replace that connection with any real depth estimator for production work. The API-format version is [`examples/api/depth_toolkit_api.json`](examples/api/depth_toolkit_api.json). -When one input contains a single image and the other contains a batch, the -single image is reused for every frame. Other unequal batch sizes produce a -clear validation error instead of silently dropping images. +The graph normalizes and repairs depth, renders a diagnostic colormap and surface normals, extracts range masks, produces a histogram, generates loopable parallax frames, and opens the live displaced-mesh viewer. Point-cloud and GLB exporters can be added anywhere after cleanup. -Mesh export bakes the current depth slider value into the vertices. GLB is the -recommended portable format; OBJ contains geometry only. +## Geometry conventions + +- Depth is interpreted in normalized `[0, 1]` space after conditioning. +- Export nodes use a pinhole camera model and configurable field of view/depth scale. +- GLB is the recommended portable mesh format; binary PLY is recommended for point clouds. +- `validity_masks` from parallax indicate pixels not introduced by camera warping and can drive compositing/inpainting. + +## Compatibility and performance + +- Python 3.10+; tested in CI on Linux, Windows, and macOS +- Real ComfyUI test: ComfyUI 0.3.60, frontend 1.26.13, Windows, NVIDIA RTX 3090 +- Conditioning/export nodes run deterministically on CPU tensors; the live viewer uses browser WebGL and is independent of CUDA, ROCm, or MPS +- 16-bit grayscale temp previews preserve depth precision; browser mesh quality is selectable to control GPU load +- No runtime network requests; images and geometry stay local ## Development ```bash -python -m pip install pytest +python -m pip install -r requirements.txt pytest build +python -m compileall -q . pytest -q +python -m build +node --check web/viewer_extension_3_0.js +node --check web/js/threeVisualizer.mjs ``` -The browser assets are vendored from Three.js 0.185.1. Its MIT license is in -`web/vendor/THREE-LICENSE.txt`. +The vendored Three.js files are MIT licensed; see `web/vendor/THREE-LICENSE.txt`. See [`SECURITY.md`](SECURITY.md) for privacy and disclosure guidance.
Cite this project -If ComfyUI Depth Visualization supports your work, please cite the software. -GitHub also provides ready-to-copy APA and BibTeX entries via **Cite this repository**. +If ComfyUI Depth Visualization supports your work, GitHub provides ready-to-copy +APA and BibTeX entries via **Cite this repository**. ```bibtex @software{Aydogan_ComfyUI_Depth_Visualization_2026, author = {Aydoğan, Gökay}, title = {ComfyUI Depth Visualization}, - version = {2.0.0}, + version = {3.0.0}, year = {2026}, url = {https://github.com/gokayfem/ComfyUI-Depth-Visualization} } diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..e70db6a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security and privacy + +This package does not collect telemetry, call remote model APIs, or transmit images, depth maps, geometry, or credentials. Runtime browser dependencies are vendored. + +Point-cloud and mesh files are resolved beneath ComfyUI's active output directory. Browser-side screenshots and mesh downloads occur only after the user presses the corresponding control. + +Do not include sensitive source media in public workflows or issue reports. Use GitHub private vulnerability reporting when available; otherwise contact the maintainer without attaching private data to a public issue. diff --git a/__init__.py b/__init__.py index 3a9de08..b9a8590 100644 --- a/__init__.py +++ b/__init__.py @@ -9,8 +9,24 @@ import folder_paths import numpy as np from PIL import Image +try: + from .depth_nodes import ( + NODE_CLASS_MAPPINGS as TOOL_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as TOOL_NODE_DISPLAY_NAME_MAPPINGS, + ) +except (ImportError, ModuleNotFoundError): # Standalone import used by pytest. + from depth_nodes import ( # type: ignore[no-redef] + NODE_CLASS_MAPPINGS as TOOL_NODE_CLASS_MAPPINGS, + NODE_DISPLAY_NAME_MAPPINGS as TOOL_NODE_DISPLAY_NAME_MAPPINGS, + ) -def _as_pil(image: Any, *, grayscale: bool = False) -> Image.Image: + +def _as_pil( + image: Any, + *, + grayscale: bool = False, + bit_depth: int = 8, +) -> Image.Image: """Convert one ComfyUI IMAGE tensor to a web-safe PIL image.""" array = image.detach().cpu().float().numpy() array = np.nan_to_num(array, nan=0.0, posinf=1.0, neginf=0.0) @@ -27,6 +43,10 @@ def _as_pil(image: Any, *, grayscale: bool = False) -> Image.Image: else: raise ValueError(f"Expected an HxW, HxWx1, or HxWx3+ image, got {array.shape}.") + if grayscale and bit_depth == 16: + if mode == "RGB": + array = array[..., 0] * 0.2126 + array[..., 1] * 0.7152 + array[..., 2] * 0.0722 + return Image.fromarray((array * 65535.0).round().astype(np.uint16)) converted = Image.fromarray((array * 255.0).round().astype(np.uint8), mode=mode) return converted.convert("L" if grayscale else "RGB") @@ -75,7 +95,8 @@ class DepthViewer: } } - RETURN_TYPES = () + RETURN_TYPES = ("IMAGE", "IMAGE") + RETURN_NAMES = ("reference_passthrough", "depth_passthrough") OUTPUT_NODE = True FUNCTION = "process_images" CATEGORY = "visualization/3D" @@ -98,6 +119,7 @@ class DepthViewer: depth = _as_pil( _batch_item(depth_map, index, batch_count, "depth_map"), grayscale=True, + bit_depth=16, ) references.append( _save_image( @@ -116,11 +138,17 @@ class DepthViewer: ) ) - return {"ui": {"reference_image": references, "depth_map": depths}} + return { + "ui": {"reference_image": references, "depth_map": depths}, + "result": (reference_image, depth_map), + } -NODE_CLASS_MAPPINGS = {"DepthViewer": DepthViewer} -NODE_DISPLAY_NAME_MAPPINGS = {"DepthViewer": "Depth Viewer"} +NODE_CLASS_MAPPINGS = {"DepthViewer": DepthViewer, **TOOL_NODE_CLASS_MAPPINGS} +NODE_DISPLAY_NAME_MAPPINGS = { + "DepthViewer": "Depth Viewer Pro", + **TOOL_NODE_DISPLAY_NAME_MAPPINGS, +} WEB_DIRECTORY = "./web" __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] diff --git a/depth_nodes.py b/depth_nodes.py new file mode 100644 index 0000000..7708542 --- /dev/null +++ b/depth_nodes.py @@ -0,0 +1,538 @@ +"""Zero-download depth processing, analysis, and 3D export nodes for ComfyUI.""" + +from __future__ import annotations + +import json +import os +import re +import struct +from datetime import datetime +from typing import Any + +import numpy as np +from PIL import Image, ImageDraw + + +CATEGORY = "depth/toolkit" +CATEGORY_3D = "depth/3D export" + + +def _numpy_batch(images: Any) -> np.ndarray: + array = images.detach().cpu().float().numpy() + array = np.nan_to_num(array, nan=0.0, posinf=1.0, neginf=0.0) + if array.ndim == 3: + array = array[None, ...] + if array.ndim != 4: + raise ValueError(f"Expected a BHWC image batch, got {array.shape}.") + if array.shape[-1] == 1: + array = np.repeat(array, 3, axis=-1) + elif array.shape[-1] < 3: + raise ValueError(f"Expected one or at least three channels, got {array.shape}.") + return np.clip(array[..., :3], 0.0, 1.0).astype(np.float32, copy=False) + + +def _depth(images: Any) -> np.ndarray: + rgb = _numpy_batch(images) + return (rgb[..., 0] * 0.2126 + rgb[..., 1] * 0.7152 + rgb[..., 2] * 0.0722).astype(np.float32) + + +def _torch(array: np.ndarray): + import torch + + return torch.from_numpy(np.ascontiguousarray(array, dtype=np.float32)) + + +def _depth_image(values: np.ndarray) -> np.ndarray: + return np.repeat(values[..., None], 3, axis=-1) + + +def _resize_batch(batch: np.ndarray, height: int, width: int) -> np.ndarray: + if batch.shape[1:3] == (height, width): + return batch + result = [] + for image in batch: + pil = Image.fromarray((image * 255.0).round().astype(np.uint8), "RGB") + result.append(np.asarray(pil.resize((width, height), Image.Resampling.LANCZOS), dtype=np.float32) / 255.0) + return np.stack(result) + + +def _safe_output_path(prefix: str, extension: str) -> str: + import folder_paths + + output_root = os.path.realpath(folder_paths.get_output_directory()) + pieces = [re.sub(r"[^A-Za-z0-9._-]+", "_", item).strip("._") for item in str(prefix).replace("\\", "/").split("/")] + pieces = [item for item in pieces if item] or ["depth_exports", "depth"] + directory = os.path.realpath(os.path.join(output_root, *pieces[:-1])) + os.makedirs(directory, exist_ok=True) + stamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f") + path = os.path.realpath(os.path.join(directory, f"{pieces[-1]}_{stamp}.{extension}")) + if os.path.commonpath((output_root, path)) != output_root: + raise ValueError("Export path must remain inside the ComfyUI output directory.") + return path + + +def _normalize_one(values: np.ndarray, method: str, low: float, high: float) -> tuple[np.ndarray, float, float]: + finite = values[np.isfinite(values)] + if finite.size == 0: + return np.zeros_like(values), 0.0, 1.0 + if method == "Percentile": + lo, hi = np.percentile(finite, [low, high]) + elif method == "Fixed range": + lo, hi = float(low), float(high) + else: + lo, hi = float(finite.min()), float(finite.max()) + if hi <= lo + 1e-12: + return np.zeros_like(values), float(lo), float(hi) + return np.clip((values - lo) / (hi - lo), 0.0, 1.0).astype(np.float32), float(lo), float(hi) + + +def _median_filter(values: np.ndarray, radius: int) -> np.ndarray: + """Precision-preserving, bounded-memory 2D median filter for BHW depth.""" + radius = int(radius) + if radius <= 0: + return values + size = radius * 2 + 1 + filtered = np.empty_like(values, dtype=np.float32) + for batch_index, image in enumerate(values): + padded = np.pad(image, ((radius, radius), (radius, radius)), mode="edge") + for row in range(image.shape[0]): + windows = np.lib.stride_tricks.sliding_window_view( + padded[row : row + size], (size, size) + ) + filtered[batch_index, row] = np.median( + windows[0], axis=(-2, -1) + ).astype(np.float32) + return filtered + + +class DepthNormalize: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "depth_map": ("IMAGE",), + "method": (["Percentile", "Min-max", "Fixed range"],), + "low": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1}), + "high": ("FLOAT", {"default": 99.0, "min": 0.0, "max": 100.0, "step": 0.1}), + "invert": ("BOOLEAN", {"default": False}), + "gamma": ("FLOAT", {"default": 1.0, "min": 0.05, "max": 8.0, "step": 0.05}), + } + } + + RETURN_TYPES = ("IMAGE", "STRING") + RETURN_NAMES = ("normalized_depth", "range_report_json") + FUNCTION = "normalize" + CATEGORY = CATEGORY + DESCRIPTION = "Normalizes arbitrary depth ranges per image with percentile clipping, inversion, and gamma." + + def normalize(self, depth_map, method, low, high, invert, gamma): + if float(high) <= float(low): + raise ValueError("high must be greater than low.") + source = _depth(depth_map) + outputs, ranges = [], [] + for image in source: + normalized, actual_low, actual_high = _normalize_one(image, method, float(low), float(high)) + if invert: + normalized = 1.0 - normalized + normalized = np.power(np.clip(normalized, 0.0, 1.0), 1.0 / float(gamma)).astype(np.float32) + outputs.append(normalized) + ranges.append({"low": actual_low, "high": actual_high}) + return (_torch(_depth_image(np.stack(outputs))), json.dumps({"method": method, "invert": bool(invert), "gamma": float(gamma), "ranges": ranges}, indent=2)) + + +_COLORMAPS = { + "viridis": [(0.0, (68, 1, 84)), (0.25, (59, 82, 139)), (0.5, (33, 145, 140)), (0.75, (94, 201, 98)), (1.0, (253, 231, 37))], + "magma": [(0.0, (0, 0, 4)), (0.25, (81, 18, 124)), (0.5, (183, 55, 121)), (0.75, (252, 137, 97)), (1.0, (252, 253, 191))], + "turbo": [(0.0, (48, 18, 59)), (0.2, (65, 105, 225)), (0.4, (52, 205, 166)), (0.6, (190, 233, 51)), (0.8, (249, 126, 32)), (1.0, (122, 4, 3))], + "plasma": [(0.0, (13, 8, 135)), (0.25, (126, 3, 168)), (0.5, (204, 71, 120)), (0.75, (248, 149, 64)), (1.0, (240, 249, 33))], + "grayscale": [(0.0, (0, 0, 0)), (1.0, (255, 255, 255))], +} + + +def _lut(name: str) -> np.ndarray: + controls = _COLORMAPS[name] + x = np.linspace(0.0, 1.0, 256) + xp = np.array([point for point, _ in controls]) + colors = np.array([color for _, color in controls], dtype=np.float32) / 255.0 + return np.stack([np.interp(x, xp, colors[:, channel]) for channel in range(3)], axis=-1).astype(np.float32) + + +class DepthColormap: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"depth_map": ("IMAGE",), "colormap": (list(_COLORMAPS),), "invert": ("BOOLEAN", {"default": False}), "show_invalid_magenta": ("BOOLEAN", {"default": True})}} + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("colored_depth",) + FUNCTION = "colorize" + CATEGORY = CATEGORY + DESCRIPTION = "Applies an embedded perceptual depth colormap without matplotlib or network access." + + def colorize(self, depth_map, colormap, invert, show_invalid_magenta): + raw = depth_map.detach().cpu().float().numpy() + invalid = ~np.isfinite(raw[..., 0] if raw.ndim == 4 else raw) + values = _depth(depth_map) + if invert: + values = 1.0 - values + indices = np.clip(np.rint(values * 255.0), 0, 255).astype(np.uint8) + output = _lut(colormap)[indices] + if show_invalid_magenta and invalid.shape == output.shape[:3]: + output[invalid] = (1.0, 0.0, 1.0) + return (_torch(output),) + + +class DepthToNormal: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"depth_map": ("IMAGE",), "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 20.0, "step": 0.05}), "field_of_view": ("FLOAT", {"default": 60.0, "min": 1.0, "max": 179.0, "step": 1.0}), "convention": (["OpenGL (+Y)", "DirectX (-Y)"],), "invert_depth": ("BOOLEAN", {"default": False})}} + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("surface_normal",) + FUNCTION = "convert" + CATEGORY = CATEGORY + DESCRIPTION = "Computes FOV-aware camera-space surface normals from a depth map." + + def convert(self, depth_map, strength, field_of_view, convention, invert_depth): + values = _depth(depth_map) + if invert_depth: + values = 1.0 - values + h, w = values.shape[1:3] + focal = 0.5 * w / np.tan(np.deg2rad(float(field_of_view)) * 0.5) + dy, dx = np.gradient(values, axis=(1, 2)) + scale = float(strength) * focal / max(w, h) + ny = -dy * scale if convention.startswith("OpenGL") else dy * scale + vectors = np.stack((-dx * scale, ny, np.ones_like(values)), axis=-1) + vectors /= np.maximum(np.linalg.norm(vectors, axis=-1, keepdims=True), 1e-8) + return (_torch(vectors * 0.5 + 0.5),) + + +class DepthRangeMask: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"depth_map": ("IMAGE",), "near": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.01}), "far": ("FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.01}), "feather": ("FLOAT", {"default": 0.02, "min": 0.0, "max": 0.5, "step": 0.005}), "invert_depth": ("BOOLEAN", {"default": False})}} + + RETURN_TYPES = ("MASK", "MASK", "IMAGE") + RETURN_NAMES = ("inside_range", "outside_range", "masked_depth") + FUNCTION = "mask" + CATEGORY = CATEGORY + DESCRIPTION = "Builds soft near/far masks for compositing, relighting, and depth-conditioned generation." + + def mask(self, depth_map, near, far, feather, invert_depth): + if float(far) <= float(near): + raise ValueError("far must be greater than near.") + values = _depth(depth_map) + if invert_depth: + values = 1.0 - values + f = max(float(feather), 1e-6) + enter = np.clip((values - (float(near) - f)) / f, 0.0, 1.0) + leave = np.clip(((float(far) + f) - values) / f, 0.0, 1.0) + inside = np.minimum(enter, leave).astype(np.float32) + return (_torch(inside), _torch(1.0 - inside), _torch(_depth_image(values * inside))) + + +class DepthCleanup: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"depth_map": ("IMAGE",), "hole_threshold": ("FLOAT", {"default": 0.001, "min": 0.0, "max": 1.0, "step": 0.001}), "fill_iterations": ("INT", {"default": 3, "min": 0, "max": 32}), "median_radius": ("INT", {"default": 1, "min": 0, "max": 5}), "preserve_edges": ("FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.05})}} + + RETURN_TYPES = ("IMAGE", "MASK", "STRING") + RETURN_NAMES = ("clean_depth", "repaired_pixels", "report_json") + FUNCTION = "clean" + CATEGORY = CATEGORY + DESCRIPTION = "Fills zero/invalid holes and suppresses speckle with an edge-preserving blend." + + def clean(self, depth_map, hole_threshold, fill_iterations, median_radius, preserve_edges): + original = _depth(depth_map) + result = original.copy() + holes = result <= float(hole_threshold) + for _ in range(int(fill_iterations)): + if not holes.any(): + break + total = np.zeros_like(result) + count = np.zeros_like(result) + for oy, ox in ((-1, 0), (1, 0), (0, -1), (0, 1)): + neighbor = np.roll(result, (oy, ox), axis=(1, 2)) + valid = neighbor > float(hole_threshold) + total += neighbor * valid + count += valid + can_fill = holes & (count > 0) + result[can_fill] = total[can_fill] / count[can_fill] + holes = result <= float(hole_threshold) + if int(median_radius) > 0: + filtered = _median_filter(result, int(median_radius)) + gradients = np.hypot(*np.gradient(original, axis=(1, 2))) + edge_weight = np.clip(gradients * 16.0 * float(preserve_edges), 0.0, 1.0) + result = filtered * (1.0 - edge_weight) + result * edge_weight + repaired = (np.abs(result - original) > 1e-6).astype(np.float32) + report = {"input_hole_fraction": float((original <= float(hole_threshold)).mean()), "unfilled_fraction": float(holes.mean()), "changed_fraction": float(repaired.mean())} + return (_torch(_depth_image(np.clip(result, 0.0, 1.0))), _torch(repaired), json.dumps(report, indent=2)) + + +class DepthStats: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"depth_map": ("IMAGE",), "bins": ("INT", {"default": 64, "min": 16, "max": 256, "step": 16})}} + + RETURN_TYPES = ("STRING", "IMAGE") + RETURN_NAMES = ("statistics_json", "histogram") + FUNCTION = "analyze" + CATEGORY = CATEGORY + DESCRIPTION = "Reports robust depth statistics and creates a workflow-visible histogram image." + + def analyze(self, depth_map, bins): + values = _depth(depth_map) + reports, charts = [], [] + for image in values: + reports.append({"min": float(image.min()), "max": float(image.max()), "mean": float(image.mean()), "median": float(np.median(image)), "std": float(image.std()), "p01": float(np.percentile(image, 1)), "p99": float(np.percentile(image, 99)), "near_fraction_0_25": float((image <= 0.25).mean()), "far_fraction_0_75": float((image >= 0.75).mean())}) + hist, _ = np.histogram(image, bins=int(bins), range=(0.0, 1.0)) + canvas = Image.new("RGB", (640, 300), (14, 18, 25)) + draw = ImageDraw.Draw(canvas) + max_count = max(int(hist.max()), 1) + for index, count in enumerate(hist): + x0 = 40 + index * 560 / len(hist) + x1 = 40 + (index + 1) * 560 / len(hist) + y = 260 - int(count / max_count * 220) + draw.rectangle((x0, y, x1 + 1, 260), fill=(55, 190, 210)) + draw.line((40, 260, 600, 260), fill=(210, 220, 230), width=2) + draw.text((40, 270), "near 0", fill=(210, 220, 230)) + draw.text((548, 270), "far 1", fill=(210, 220, 230)) + charts.append(np.asarray(canvas, dtype=np.float32) / 255.0) + return (json.dumps({"batch": reports}, indent=2), _torch(np.stack(charts))) + + +def _camera_points(depth: np.ndarray, fov: float, depth_scale: float, stride: int) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + h, w = depth.shape + ys = np.arange(0, h, stride, dtype=np.int32) + xs = np.arange(0, w, stride, dtype=np.int32) + xx, yy = np.meshgrid(xs, ys) + z = depth[yy, xx] * float(depth_scale) + focal = 0.5 * w / np.tan(np.deg2rad(float(fov)) * 0.5) + x = (xx - (w - 1) * 0.5) * z / focal + y = -((yy - (h - 1) * 0.5) * z / focal) + return np.stack((x, y, z), axis=-1).astype(np.float32), xs, ys + + +class DepthToPointCloud: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"depth_map": ("IMAGE",), "field_of_view": ("FLOAT", {"default": 60.0, "min": 1.0, "max": 179.0, "step": 1.0}), "depth_scale": ("FLOAT", {"default": 1.0, "min": 0.001, "max": 1000.0, "step": 0.01}), "stride": ("INT", {"default": 4, "min": 1, "max": 64}), "drop_zero_depth": ("BOOLEAN", {"default": True}), "filename_prefix": ("STRING", {"default": "depth_exports/point_cloud"})}, "optional": {"color_image": ("IMAGE",)}} + + RETURN_TYPES = ("STRING", "STRING") + RETURN_NAMES = ("ply_paths", "manifest_json") + FUNCTION = "export" + CATEGORY = CATEGORY_3D + OUTPUT_NODE = True + DESCRIPTION = "Exports binary PLY point clouds with camera-space coordinates and optional RGB color." + + def export(self, depth_map, field_of_view, depth_scale, stride, drop_zero_depth, filename_prefix, color_image=None): + depths = _depth(depth_map) + colors = _resize_batch(_numpy_batch(color_image), depths.shape[1], depths.shape[2]) if color_image is not None else None + paths, counts = [], [] + for batch_index, depth in enumerate(depths): + points, xs, ys = _camera_points(depth, field_of_view, depth_scale, int(stride)) + points = points.reshape(-1, 3) + valid = np.isfinite(points).all(axis=1) + if drop_zero_depth: + valid &= points[:, 2] > 1e-8 + points = points[valid] + rgb = None + if colors is not None: + color = colors[min(batch_index, len(colors) - 1)][np.ix_(ys, xs)].reshape(-1, 3) + rgb = (color[valid] * 255.0).round().astype(np.uint8) + path = _safe_output_path(f"{filename_prefix}_{batch_index:03d}", "ply") + properties = "property float x\nproperty float y\nproperty float z\n" + if rgb is not None: + properties += "property uchar red\nproperty uchar green\nproperty uchar blue\n" + header = f"ply\nformat binary_little_endian 1.0\ncomment ComfyUI Depth Visualization\nelement vertex {len(points)}\n{properties}end_header\n".encode("ascii") + with open(path, "xb") as handle: + handle.write(header) + if rgb is None: + handle.write(points.astype(" None: + while len(blob) % alignment: + blob.append(0) + + +def _write_glb(path: str, positions: np.ndarray, colors: np.ndarray, indices: np.ndarray) -> None: + binary = bytearray() + pos_offset = len(binary) + binary.extend(positions.astype(" 0: + edges = (np.linalg.norm(vertices[0] - vertices[1]), np.linalg.norm(vertices[1] - vertices[2]), np.linalg.norm(vertices[2] - vertices[0])) + if max(edges) > threshold: + continue + indices.extend(tri) + index_array = np.asarray(indices, dtype=np.uint32) + if len(index_array) < 3: + raise ValueError("No valid mesh triangles remain; increase max_edge_length or reduce stride.") + path = _safe_output_path(f"{filename_prefix}_{batch_index:03d}", "glb") + _write_glb(path, positions, rgb, index_array) + paths.append(path) + triangle_counts.append(len(index_array) // 3) + return ("\n".join(paths), json.dumps({"format": "glTF 2.0 binary", "paths": paths, "triangle_counts": triangle_counts, "fov_degrees": float(field_of_view), "depth_scale": float(depth_scale), "stride": int(stride), "max_edge_length": float(max_edge_length)}, indent=2)) + + +def _bilinear_clamp(image: np.ndarray, x: np.ndarray, y: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + h, w = image.shape[:2] + valid = (x >= 0.0) & (x <= w - 1.0) & (y >= 0.0) & (y <= h - 1.0) + x = np.clip(x, 0.0, w - 1.0) + y = np.clip(y, 0.0, h - 1.0) + x0, y0 = np.floor(x).astype(np.int32), np.floor(y).astype(np.int32) + x1, y1 = np.minimum(x0 + 1, w - 1), np.minimum(y0 + 1, h - 1) + wx, wy = (x - x0)[..., None], (y - y0)[..., None] + top = image[y0, x0] * (1 - wx) + image[y0, x1] * wx + bottom = image[y1, x0] * (1 - wx) + image[y1, x1] * wx + return top * (1 - wy) + bottom * wy, valid.astype(np.float32) + + +class DepthParallaxFrames: + @classmethod + def INPUT_TYPES(cls): + return {"required": {"image": ("IMAGE",), "depth_map": ("IMAGE",), "frames": ("INT", {"default": 24, "min": 2, "max": 240}), "amplitude_px": ("FLOAT", {"default": 24.0, "min": 0.0, "max": 512.0, "step": 1.0}), "path": (["horizontal sway", "vertical sway", "ellipse", "dolly"],), "loop": ("BOOLEAN", {"default": True}), "depth_center": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01})}} + + RETURN_TYPES = ("IMAGE", "MASK", "STRING") + RETURN_NAMES = ("frames", "validity_masks", "manifest_json") + FUNCTION = "animate" + CATEGORY = CATEGORY + DESCRIPTION = "Creates video-ready depth parallax frames plus validity masks for inpainting exposed edges." + + def animate(self, image, depth_map, frames, amplitude_px, path, loop, depth_center): + colors = _numpy_batch(image) + depths = _depth(depth_map) + count = max(len(colors), len(depths)) + if len(colors) not in (1, count) or len(depths) not in (1, count): + raise ValueError("image and depth_map batches must match or contain one image.") + depths = _resize_batch(_depth_image(depths), colors.shape[1], colors.shape[2])[..., 0] + h, w = colors.shape[1:3] + yy, xx = np.mgrid[0:h, 0:w] + outputs, masks = [], [] + frame_count = int(frames) + denominator = frame_count if loop else max(frame_count - 1, 1) + for batch_index in range(count): + color = colors[min(batch_index, len(colors) - 1)] + depth = depths[min(batch_index, len(depths) - 1)] + relative = (depth - float(depth_center)) * float(amplitude_px) + for frame in range(frame_count): + phase = 2.0 * np.pi * frame / denominator + sx = sy = 0.0 + scale = 0.0 + if path == "horizontal sway": + sx = np.sin(phase) + elif path == "vertical sway": + sy = np.sin(phase) + elif path == "ellipse": + sx, sy = np.cos(phase), np.sin(phase) * 0.55 + else: + scale = np.sin(phase) + sample_x = xx - relative * sx - (xx - (w - 1) * 0.5) * relative * scale / max(w, 1) + sample_y = yy - relative * sy - (yy - (h - 1) * 0.5) * relative * scale / max(h, 1) + warped, valid = _bilinear_clamp(color, sample_x, sample_y) + outputs.append(warped) + masks.append(valid) + manifest = {"source_batches": count, "frames_per_source": frame_count, "total_frames": len(outputs), "path": path, "loop": bool(loop), "amplitude_px": float(amplitude_px), "mask_semantics": "1 = sampled inside source; 0 = exposed edge requiring fill"} + return (_torch(np.stack(outputs)), _torch(np.stack(masks)), json.dumps(manifest, indent=2)) + + +NODE_CLASS_MAPPINGS = { + "DepthNormalize": DepthNormalize, + "DepthColormap": DepthColormap, + "DepthToNormal": DepthToNormal, + "DepthRangeMask": DepthRangeMask, + "DepthCleanup": DepthCleanup, + "DepthStats": DepthStats, + "DepthToPointCloud": DepthToPointCloud, + "DepthToMesh": DepthToMesh, + "DepthParallaxFrames": DepthParallaxFrames, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "DepthNormalize": "Normalize Depth", + "DepthColormap": "Depth Colormap", + "DepthToNormal": "Depth to Surface Normal", + "DepthRangeMask": "Depth Range Masks", + "DepthCleanup": "Clean & Repair Depth", + "DepthStats": "Analyze Depth", + "DepthToPointCloud": "Export Depth Point Cloud (PLY)", + "DepthToMesh": "Export Depth Mesh (GLB)", + "DepthParallaxFrames": "Depth Parallax Frames", +} diff --git a/docs/assets/live-comfyui.png b/docs/assets/live-comfyui.png new file mode 100644 index 0000000000000000000000000000000000000000..9dcb1b243bdc69c84d7d021021c856f2de2c66e8 GIT binary patch literal 101267 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 ixrz5$vLQ|j!MO*; 0 + assert json.loads(stats)["batch"][0]["max"] <= 1 + assert histogram.shape == (1, 300, 640, 3) + + +def test_median_filter_preserves_sub_8_bit_depth_precision(): + values = np.array( + [[[0.5001, 0.5002, 0.5003], [0.5004, 0.5005, 0.5006], [0.5007, 0.5008, 0.5009]]], + dtype=np.float32, + ) + filtered = nodes._median_filter(values, 1) + assert np.isclose(filtered[0, 1, 1], 0.5005, atol=1e-6) + assert not np.isclose(filtered[0, 1, 1] * 255, round(filtered[0, 1, 1] * 255)) + + +def test_binary_ply_and_glb_exports(monkeypatch, tmp_path): + import folder_paths + + monkeypatch.setattr(folder_paths, "get_output_directory", lambda: str(tmp_path), raising=False) + ply_paths, ply_manifest = nodes.DepthToPointCloud().export( + depth(), 60, 2, 4, True, "depth_exports/cloud", color() + ) + glb_paths, glb_manifest = nodes.DepthToMesh().export( + depth(), 60, 2, 4, 100, "depth_exports/mesh", color() + ) + ply = Path(ply_paths) + glb = Path(glb_paths) + assert ply.read_bytes().startswith(b"ply\nformat binary_little_endian") + magic, version, total = struct.unpack("<4sII", glb.read_bytes()[:12]) + assert (magic, version, total) == (b"glTF", 2, glb.stat().st_size) + assert json.loads(ply_manifest)["point_counts"][0] > 0 + assert json.loads(glb_manifest)["triangle_counts"][0] > 0 + + +def test_parallax_frames_and_validity_masks(): + frames, masks, manifest = nodes.DepthParallaxFrames().animate( + color(), depth(), 8, 12, "ellipse", True, 0.5 + ) + assert frames.shape == (8, 24, 32, 3) + assert masks.shape == (8, 24, 32) + assert json.loads(manifest)["total_frames"] == 8 + assert masks.min() == 0 and masks.max() == 1 diff --git a/web/html/threeVisualizer.html b/web/html/threeVisualizer.html index 640aa02..73fa15d 100644 --- a/web/html/threeVisualizer.html +++ b/web/html/threeVisualizer.html @@ -35,10 +35,19 @@ min="-5" max="5" step="0.05" - value="2" + value="1.25" > - 2.00 + 1.25 + +