Skip to content

feat(memory): add depth codecs and default to lossless JPEG XL - #3637

Draft
TomCC7 wants to merge 7 commits into
mainfrom
cc/feat/better-depth-encoding
Draft

feat(memory): add depth codecs and default to lossless JPEG XL#3637
TomCC7 wants to merge 7 commits into
mainfrom
cc/feat/better-depth-encoding

Conversation

@TomCC7

@TomCC7 TomCC7 commented Aug 24, 2026

Copy link
Copy Markdown
Member

Contribution path

  • Small, safe change that does not need a tracking issue

Problem

The default Image codec sends every image through ordinary lossy JPEG, which
corrupts metric depth. Some recorders avoid that path with generic LZ4, but LZ4
leaves substantial size savings on the table and provides no bounded-error option.

Solution

  • Keep the existing JpegCodec and jpeg codec ID. Visual images retain the
    original lossy JPEG bytes and LCM Image envelope; DEPTH/float32 and
    DEPTH16/uint16 use lossless JPEG XL in that same envelope.
  • Add the opt-in lerc codec with a fixed 5 mm maximum error. It supports
    float32 meters and uint16 millimeters while preserving metadata and validity masks.
  • Let WorldBelief and Spot depth recording use the format-aware default. Spot
    grayscale remains explicitly lossless with lz4+lcm.
  • Add a full-stream benchmark that exercises only supported in-repo codecs:
    lcm, lz4+lcm, jpeg, and lerc.
  • Print one final Rich benchmark report with per-stream results and decision
    highlights, while retaining complete JSON and Markdown artifacts.
  • Document the ROS 2, RealSense colorization, MCAP, LERC, and JPEG XL tradeoffs.

Benchmark workflow

The runner processes every frame in every detected depth stream through the
production codec objects. It measures encoded size, encode/decode wall and
process-CPU time, and metric fidelity, then writes results.json and results.md.
After all streams finish, it prints one compact terminal report with wall-time,
fidelity, and exact-codec highlights. It fails if metadata changes, an exact codec
changes depth values, LERC exceeds 5 mm, the valid mask changes, or a stream
changes dtype, shape, or format.

Real-recording benchmark

────────────────────────────────────── Depth codec benchmark ───────────────────────────────────────
Inputs    spot_small_loop.db, sf_office_stairs.db
Streams   6
Frames    19822
Elapsed   341.52 s
Output    /tmp/depth-codecs-mobile
Artifacts results.json, results.md

────────────────────────────── spot_small_loop.db — depth_image_back ───────────────────────────────
3316 frames · 240x424 · uint16 · DEPTH16
╭─────────┬───────────┬───────┬─────────────┬─────────────┬───────────────╮
│         │           │       │   Encode ms │   Decode ms │               │
│ Codec   │ Avg/frame │ Ratio │   p50 / p95 │   p50 / p95 │ Fidelity      │
├─────────┼───────────┼───────┼─────────────┼─────────────┼───────────────┤
│ lcm     │ 198.8 KiB │ 1.00x │ 0.02 / 0.02 │ 0.01 / 0.02 │ exact         │
│ lz4+lcm │  79.4 KiB │ 2.50x │ 0.34 / 0.41 │ 0.08 / 0.10 │ exact         │
│ jpeg    │  54.4 KiB │ 3.65x │ 0.59 / 0.69 │ 0.79 / 0.92 │ exact         │
│ lerc    │  39.5 KiB │ 5.03x │ 1.79 / 2.15 │ 0.53 / 0.63 │ ≤5 mm         │
│         │           │       │             │             │ RMSE 2.920 mm │
╰─────────┴───────────┴───────┴─────────────┴─────────────┴───────────────╯
Highlights
• Best exact compression: jpeg — 3.65x
• Fastest exact encode: lcm — 0.02 ms p50
• Fastest exact decode: lcm — 0.01 ms p50
• LERC (≤5 mm): 5.03x — RMSE 2.920 mm

─────────────────────────── spot_small_loop.db — depth_image_front_left ────────────────────────────
3316 frames · 424x240 · uint16 · DEPTH16
╭─────────┬───────────┬───────┬─────────────┬─────────────┬───────────────╮
│         │           │       │   Encode ms │   Decode ms │               │
│ Codec   │ Avg/frame │ Ratio │   p50 / p95 │   p50 / p95 │ Fidelity      │
├─────────┼───────────┼───────┼─────────────┼─────────────┼───────────────┤
│ lcm     │ 198.8 KiB │ 1.00x │ 0.02 / 0.02 │ 0.01 / 0.02 │ exact         │
│ lz4+lcm │  89.2 KiB │ 2.23x │ 0.36 / 0.41 │ 0.08 / 0.09 │ exact         │
│ jpeg    │  55.4 KiB │ 3.59x │ 0.60 / 0.66 │ 0.80 / 0.90 │ exact         │
│ lerc    │  39.3 KiB │ 5.06x │ 1.78 / 2.07 │ 0.57 / 0.63 │ ≤5 mm         │
│         │           │       │             │             │ RMSE 2.914 mm │
╰─────────┴───────────┴───────┴─────────────┴─────────────┴───────────────╯
Highlights
• Best exact compression: jpeg — 3.59x
• Fastest exact encode: lcm — 0.02 ms p50
• Fastest exact decode: lcm — 0.01 ms p50
• LERC (≤5 mm): 5.06x — RMSE 2.914 mm

─────────────────────────── spot_small_loop.db — depth_image_front_right ───────────────────────────
3316 frames · 424x240 · uint16 · DEPTH16
╭─────────┬───────────┬───────┬─────────────┬─────────────┬───────────────╮
│         │           │       │   Encode ms │   Decode ms │               │
│ Codec   │ Avg/frame │ Ratio │   p50 / p95 │   p50 / p95 │ Fidelity      │
├─────────┼───────────┼───────┼─────────────┼─────────────┼───────────────┤
│ lcm     │ 198.8 KiB │ 1.00x │ 0.02 / 0.02 │ 0.01 / 0.02 │ exact         │
│ lz4+lcm │  85.9 KiB │ 2.32x │ 0.35 / 0.40 │ 0.08 / 0.09 │ exact         │
│ jpeg    │  57.0 KiB │ 3.49x │ 0.59 / 0.64 │ 0.78 / 0.87 │ exact         │
│ lerc    │  37.8 KiB │ 5.26x │ 1.88 / 2.07 │ 0.61 / 0.68 │ ≤5 mm         │
│         │           │       │             │             │ RMSE 2.908 mm │
╰─────────┴───────────┴───────┴─────────────┴─────────────┴───────────────╯
Highlights
• Best exact compression: jpeg — 3.49x
• Fastest exact encode: lcm — 0.02 ms p50
• Fastest exact decode: lcm — 0.01 ms p50
• LERC (≤5 mm): 5.26x — RMSE 2.908 mm

────────────────────────────── spot_small_loop.db — depth_image_left ───────────────────────────────
3316 frames · 240x424 · uint16 · DEPTH16
╭─────────┬───────────┬───────┬─────────────┬─────────────┬───────────────╮
│         │           │       │   Encode ms │   Decode ms │               │
│ Codec   │ Avg/frame │ Ratio │   p50 / p95 │   p50 / p95 │ Fidelity      │
├─────────┼───────────┼───────┼─────────────┼─────────────┼───────────────┤
│ lcm     │ 198.8 KiB │ 1.00x │ 0.02 / 0.02 │ 0.01 / 0.02 │ exact         │
│ lz4+lcm │  85.9 KiB │ 2.31x │ 0.36 / 0.40 │ 0.09 / 0.10 │ exact         │
│ jpeg    │  54.0 KiB │ 3.68x │ 0.61 / 0.68 │ 0.81 / 0.91 │ exact         │
│ lerc    │  39.3 KiB │ 5.05x │ 1.72 / 1.99 │ 0.50 / 0.59 │ ≤5 mm         │
│         │           │       │             │             │ RMSE 2.944 mm │
╰─────────┴───────────┴───────┴─────────────┴─────────────┴───────────────╯
Highlights
• Best exact compression: jpeg — 3.68x
• Fastest exact encode: lcm — 0.02 ms p50
• Fastest exact decode: lcm — 0.01 ms p50
• LERC (≤5 mm): 5.05x — RMSE 2.944 mm

────────────────────────────── spot_small_loop.db — depth_image_right ──────────────────────────────
3316 frames · 240x424 · uint16 · DEPTH16
╭─────────┬───────────┬───────┬─────────────┬─────────────┬───────────────╮
│         │           │       │   Encode ms │   Decode ms │               │
│ Codec   │ Avg/frame │ Ratio │   p50 / p95 │   p50 / p95 │ Fidelity      │
├─────────┼───────────┼───────┼─────────────┼─────────────┼───────────────┤
│ lcm     │ 198.8 KiB │ 1.00x │ 0.02 / 0.02 │ 0.01 / 0.02 │ exact         │
│ lz4+lcm │  78.3 KiB │ 2.54x │ 0.35 / 0.39 │ 0.09 / 0.10 │ exact         │
│ jpeg    │  51.9 KiB │ 3.83x │ 0.61 / 0.68 │ 0.80 / 0.90 │ exact         │
│ lerc    │  37.4 KiB │ 5.32x │ 1.82 / 2.17 │ 0.53 / 0.61 │ ≤5 mm         │
│         │           │       │             │             │ RMSE 2.895 mm │
╰─────────┴───────────┴───────┴─────────────┴─────────────┴───────────────╯
Highlights
• Best exact compression: jpeg — 3.83x
• Fastest exact encode: lcm — 0.02 ms p50
• Fastest exact decode: lcm — 0.01 ms p50
• LERC (≤5 mm): 5.32x — RMSE 2.895 mm

─────────────────────────── sf_office_stairs.db — realsense_depth_image ────────────────────────────
3242 frames · 480x848 · uint16 · DEPTH16
╭─────────┬───────────┬───────┬─────────────┬─────────────┬───────────────╮
│         │           │       │   Encode ms │   Decode ms │               │
│ Codec   │ Avg/frame │ Ratio │   p50 / p95 │   p50 / p95 │ Fidelity      │
├─────────┼───────────┼───────┼─────────────┼─────────────┼───────────────┤
│ lcm     │ 795.1 KiB │ 1.00x │ 0.07 / 0.09 │ 0.04 / 0.10 │ exact         │
│ lz4+lcm │ 256.1 KiB │ 3.10x │ 1.12 / 1.56 │ 0.30 / 0.38 │ exact         │
│ jpeg    │ 146.1 KiB │ 5.44x │ 1.89 / 2.27 │ 2.66 / 3.18 │ exact         │
│ lerc    │ 140.9 KiB │ 5.64x │ 5.92 / 7.07 │ 1.42 / 1.64 │ ≤5 mm         │
│         │           │       │             │             │ RMSE 2.824 mm │
╰─────────┴───────────┴───────┴─────────────┴─────────────┴───────────────╯
Highlights
• Best exact compression: jpeg — 5.44x
• Fastest exact encode: lcm — 0.07 ms p50
• Fastest exact decode: lcm — 0.04 ms p50
• LERC (≤5 mm): 5.64x — RMSE 2.824 mm

How to test

Quick synthetic check:

uv run python -m dimos.memory.codecs.tool_depth_benchmark --synthetic uint16
uv run python -m dimos.memory.codecs.tool_depth_benchmark --synthetic float32

Reproduce the three-recording run:

uv run python -m dimos.memory.codecs.tool_depth_benchmark \
  g1_zed rgbd_frames \
  data/xarm6_worldbelief_realsense_d435i_stationery_calibrated/xarm6_worldbelief_20260729_203624_161992.db \
  --output /tmp/depth-codecs

Validation performed:

uv run pytest dimos/memory -q
./bin/run-doc-codeblocks --ci --no-cache docs/capabilities/memory/depth_compression.md
uv run md-babel-py run docs/capabilities/memory/index.md --stdout \
  --execution-timeout 120 --lang python,sh,node
uv run ruff check dimos/msgs/sensor_msgs/Image.py dimos/memory/codecs \
  dimos/experimental/robot/bosdyn/spot
uv run mypy dimos/msgs/sensor_msgs/Image.py dimos/memory/codecs/base.py \
  dimos/memory/codecs/jpeg.py dimos/memory/codecs/lerc.py \
  dimos/memory/codecs/tool_depth_benchmark.py

487 passed; both executable documentation paths, Ruff, formatting, diff checks,
and mypy completed without errors.

AI assistance

OpenAI Codex with GPT-5 handled the research, implementation, tests, benchmarks,
documentation, and PR draft under user direction.

Checklist

  • I have read and approved the CLA.

@mintlify

mintlify Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
dimensional 🟢 Ready View Preview Aug 24, 2026, 5:58 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.86441% with 93 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/memory/codecs/tool_depth_benchmark.py 79.24% 77 Missing and 5 partials ⚠️
dimos/msgs/sensor_msgs/Image.py 82.35% 3 Missing and 3 partials ⚠️
dimos/memory/codecs/lerc.py 95.12% 1 Missing and 1 partial ⚠️
dimos/memory/codecs/test_codecs.py 95.34% 1 Missing and 1 partial ⚠️
dimos/memory/codecs/base.py 80.00% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main    #3637      +/-   ##
==========================================
+ Coverage   77.26%   77.36%   +0.09%     
==========================================
  Files        1262     1274      +12     
  Lines      120302   121642    +1340     
  Branches    10600    10844     +244     
==========================================
+ Hits        92953    94110    +1157     
- Misses      24284    24469     +185     
+ Partials     3065     3063       -2     
Flag Coverage Δ
OS-ubuntu-24.04-arm 72.29% <82.34%> (+0.13%) ⬆️
OS-ubuntu-latest 74.16% <82.34%> (+0.10%) ⬆️
Py-3.10 74.15% <82.34%> (+0.10%) ⬆️
Py-3.11 74.15% <82.34%> (+0.10%) ⬆️
Py-3.12 74.16% <82.34%> (+0.10%) ⬆️
Py-3.13 74.16% <82.34%> (+0.10%) ⬆️
Py-3.14 74.16% <82.34%> (+0.10%) ⬆️
Py-3.14t 74.16% <82.34%> (+0.10%) ⬆️
SelfHosted-Linux 35.06% <26.87%> (-0.09%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/experimental/robot/bosdyn/spot/recorder.py 100.00% <100.00%> (ø)
.../experimental/world_belief/worldbelief_recorder.py 68.62% <ø> (+3.17%) ⬆️
dimos/memory/codecs/jpeg.py 100.00% <100.00%> (ø)
dimos/memory/codecs/test_depth_benchmark.py 100.00% <100.00%> (ø)
dimos/memory/codecs/test_lerc.py 100.00% <100.00%> (ø)
dimos/memory/module.py 57.62% <ø> (+1.10%) ⬆️
dimos/memory/codecs/base.py 87.30% <80.00%> (+6.26%) ⬆️
dimos/memory/codecs/lerc.py 95.12% <95.12%> (ø)
dimos/memory/codecs/test_codecs.py 95.20% <95.34%> (+4.91%) ⬆️
dimos/msgs/sensor_msgs/Image.py 68.33% <82.35%> (+8.62%) ⬆️
... and 1 more

... and 24 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@TomCC7 TomCC7 changed the title feat(memory): add LERC and Zstd depth codecs feat(memory): add depth codecs and default to lossless JPEG XL Aug 24, 2026
Comment thread dimos/experimental/robot/bosdyn/spot/recorder.py
Comment thread dimos/memory/codecs/base.py Outdated
Comment thread dimos/memory/codecs/jpeg.py Outdated
@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change adds lossless JPEG XL depth storage, an opt-in LERC codec, updated depth recorder defaults, benchmark tooling, tests, and documentation.

Two issues need resolution before merging: benchmark dataset loading can execute code embedded in pickle files, and newly written JPEG XL depth records retain the legacy jpeg storage identity even though older DIMOS readers cannot decode their jxl;depth* envelopes.

T-Rex validation blocked

The pickle failure path executed and showed code running before payload rejection, but the artifact-upload tool was unavailable. The missing tool prevented attaching the required published evidence reference for that result.

Confidence Score: 2/5

Not safe to merge until untrusted benchmark input is no longer executable and the persisted JPEG XL depth format has an explicit compatibility strategy.

The JPEG XL reader incompatibility was reproduced with a real current encoding and the pre-change decode path. The pickle behavior was also exercised directly, but its evidence artifact could not be published because the upload tool was unavailable.

Files Needing Attention: dimos/memory/codecs/tool_depth_benchmark.py and dimos/memory/codecs/jpeg.py

Security Review

Depth benchmark input accepts discovered pickle files and deserializes them before validating their payload structure. A crafted dataset file can execute code with the benchmark operator's privileges.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced proof for a posted P1 finding and linked it to the corresponding review comment.
  • Supporting artifacts for the P1 finding were uploaded, including the reproduction source and decoder capture logs.
  • Post-change compatibility flow was validated, showing the after-change decoder succeeds and the pre-change baseline behavior is captured in the logs.
  • T-Rex produced proof for a second posted P1 finding.
  • The benchmark pickle path validation could not be completed because the artifact-upload tool was unavailable after the execution result.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 Unversioned jpeg persistence ID cannot describe JPEG XL depth envelopes to pre-change readers

    • Bug
      • A DEPTH16 frame encoded by the current JpegCodec persists under codec ID jpeg but contains an LCM Image envelope marked jxl;depth16. A DIMOS reader from before commit 2ffe88d11 has no JPEG XL envelope dispatch. When given the newly encoded payload it raises ValueError: Unsupported encoding: jxl;depth16, so persisted depth records cannot be read by that older decoder.
    • Cause
      • dimos/memory/codecs/jpeg.py:36-38 changed the wire payload selected by the existing JpegCodec from JPEG to JPEG XL for depth formats, while codec_id() still derives the same class-name ID jpeg. The base-parent Image.lcm_decode only special-cased jpeg; JPEG XL envelope decoding was introduced separately in dimos/msgs/sensor_msgs/Image.py after the parent revision.
    • Fix
      • Version or distinguish the persisted codec/format contract for JPEG XL depth payloads (for example a new codec ID) and provide an explicit compatibility/migration strategy. Do not rely on the unchanged jpeg ID to select a decoder that predates jxl;depth and jxl;depth16 envelope support.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin/cc/..." | Re-trigger Greptile



def _pickle_frame(path: Path, source: Path) -> Image:
payload = pickle.loads(path.read_bytes())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Unrestricted pickle dataset execution

Benchmarking a directory or named dataset deserializes every discovered depth/*.pickle file with pickle.loads before checking whether the decoded value has an accepted type or shape. A crafted pickle can therefore execute its reconstruction hook with the benchmark process's privileges even though it is subsequently rejected as an invalid depth payload. Do not deserialize discovered dataset files with unrestricted pickle; use a non-executable frame format or a strictly allowlisted legacy decoder.

Comment on lines +36 to 38
if value.format in (ImageFormat.DEPTH, ImageFormat.DEPTH16):
return value.lcm_jpegxl_encode(effort=1)
return value.lcm_jpeg_encode(quality=self._quality)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unversioned JPEG XL depth envelope

JpegCodec still persists under the jpeg codec ID, but depth frames now carry jxl;depth or jxl;depth16 envelopes. A reader from before JPEG XL envelope dispatch still selects the legacy jpeg codec and raises ValueError: Unsupported encoding: jxl;depth16 for these newly written records. Version or distinguish this persisted format transition and provide an explicit compatibility or migration path so older readers do not treat JPEG XL depth data as legacy JPEG.

Artifacts

JPEG codec compatibility reproduction source

  • This authored harness encodes a DEPTH16 frame through the real current codec and applies the exact pre-JPEG-XL decoder branch, Takeaway.

Base-parent JPEG codec and decoder capture

  • This command capture shows the base-parent implementation before JPEG XL envelope dispatch existed, Takeaway.

Current depth payload through pre-change decoder

  • This executed run proves the same newly encoded depth payload fails in the pre-change path and succeeds in the current path, Takeaway.

View artifacts

T-Rex Ran code and verified through T-Rex

@TomCC7
TomCC7 marked this pull request as draft August 24, 2026 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants