Skip to content

Conversation

@makeecat
Copy link

Related

What

This is a draft PR to provide prototype for EncodedDepthImage. This PR needs to be split into sub-PRs once reviewed by @oxkitsune

  1. Implemented the EncodedDepthImage archetype
  2. Introduced a new utility crate re_depth_compression to host rvl codecs implementation
  3. Integrated EncodedDepthImage archetype into viewer
  4. Supported the EncodedDepthImage archetype in re_mcap.

@oxkitsune oxkitsune self-requested a review November 12, 2025 16:29
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Hi! Thanks for opening this pull request.

Because this is your first time contributing to this repository, make sure you've read our Contributor Guide and Code of Conduct.

@makeecat
Copy link
Author

Hi @oxkitsune , I believe I am comfortable with the prototype, the proof concept works well with lint and my local mcap bag with RVL-encoded depth image. I am ready to submit a separate PR for EncodedDepthImage archetype.

However, I am not very confident about my changes in re_view_spatial crate. I refactored the crates/viewer/re_view_spatial/src/visualizers/depth_images.rs to support crates/viewer/re_view_spatial/src/visualizers/encoded_depth_images.rs as a separate visualizer. I am looking for comments on that part.

If you are happy with the proof-of-concept demo and the design of the EncodedDepthImage archetype, I can prepare the first PR for EncodedDepthImage archetype.

@makeecat makeecat marked this pull request as ready for review November 13, 2025 19:30
@oxkitsune
Copy link
Member

This demo looks great, thanks for putting it together!

Go ahead and open the PR for the EncodedDepthImage archetype; I’ll leave any implementation notes there.

@makeecat
Copy link
Author

According to our discussion, we will not split this PR into small PRs.

There are a few decisions we need to make:

  1. whether add mediatype
  2. whether leave re_depth_compression as a crate or integrate it into exsting logic
    fallback detection of different format for decoding if the data didn't provide type metadata
  3. When RVL metadata disagrees with the logged ImageFormat, the code just logs a warning and still constructs an ImageInfo with the caller’s width/height.
  4. I don't like the way I refactored the DepthImagevisualizer: viewer/re_view_spatial/src/visualizers/depth_image.rs, I would like to avoid changing too much of existing depth image visualizer, but I didn't come up with a solution. The reason I have to refactor it is to expose some components from the original depth image visualizer to support encoded depth image visualizer.

@makeecat
Copy link
Author

After discussing with @oxkitsune

  1. We plan to add support of rvl as a media_type
  2. We plan to squash the logic of re_depth_completion crate into re_viewer_context.
  3. We plan to support MediaType::guess_from_data to support fallback detection of RVL data
  4. We need further discussion to avoid refactoring too much of viewer/re_view_spatial/src/visualizers/depth_image.rs

@oxkitsune oxkitsune added 📺 re_viewer affects re_viewer itself include in changelog and removed do-not-merge Do not merge this PR labels Nov 24, 2025
Comment on lines 5 to 7
pub use ros_rvl::{
RvlDecodeError, RvlMetadata, decode_ros_rvl_f32, decode_ros_rvl_u16, parse_ros_rvl_metadata,
};
Copy link
Member

Choose a reason for hiding this comment

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

If the module's already pub, I think it's neater to avoid re-exporting. This means that in the future, when adding more formats, we can easily access it using re_depth_compression::<format>.

Copy link
Author

Choose a reason for hiding this comment

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

done

Copy link
Author

Choose a reason for hiding this comment

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

done

PayloadLengthMismatch { width: u32, height: u32 },
}

fn parse_ros_rvl_metadata(data: &[u8]) -> Result<RvlMetadata, RvlDecodeError> {
Copy link
Member

Choose a reason for hiding this comment

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

IIUC this is the same metadata parsing from crates/viewer/re_viewer_context/src/utils/depth.rs right? Is there any way we can avoid duplicating this?

Copy link
Author

Choose a reason for hiding this comment

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

done, refactored into re_depth_compression

format.datatype()
)));
}

Copy link
Member

Choose a reason for hiding this comment

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

Would be a good place to validate that the decoded_format is actually single channel data. This now support RGB pngs

Copy link
Author

Choose a reason for hiding this comment

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

now we have enforced single-channel PNG

// Read width and height from bytes 12-15 and 16-19 respectively
let width = u32::from_le_bytes([buf[12], buf[13], buf[14], buf[15]]);
let height = u32::from_le_bytes([buf[16], buf[17], buf[18], buf[19]]);

Copy link
Member

Choose a reason for hiding this comment

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

Given the structure given above, I think that this, in its current state, will lead to a lot of false positives.
I think we should also check that depth_quant_a and depth_quant_b are both valid floats with sane ranges (e.g. not NaN or inf and not absurdly large)

Suggested change
let depth_quant_a = f32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
let depth_quant_b = f32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]);
// Reject NaNs/infs
if !depth_quant_a.is_finite() || !depth_quant_b.is_finite() {
return false;
}
// Reject insane magnitudes
if depth_quant_a <= 0.0 || depth_quant_a > 1e4 || depth_quant_b.abs() > 1e4 {
return false;
}

Copy link
Author

Choose a reason for hiding this comment

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

done

}

#[test]
fn test_guess_from_data() {
Copy link
Member

Choose a reason for hiding this comment

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

most of this test is for MediaType::rvl(), which I think is fine. No need to test the GLB/stl here in my opinion. Test can also be renamed to test_guess_from_data_rvl

Copy link
Author

Choose a reason for hiding this comment

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

done

Comment on lines 20 to 24
/// Image format (width, height, datatype).
///
/// Unlike generic [archetypes.EncodedImage], most depth codecs do not carry full headers,
/// so width/height/datatype must be logged explicitly.
format: rerun.components.ImageFormat ("attr.rerun.component_required", order: 1100);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// Image format (width, height, datatype).
///
/// Unlike generic [archetypes.EncodedImage], most depth codecs do not carry full headers,
/// so width/height/datatype must be logged explicitly.
format: rerun.components.ImageFormat ("attr.rerun.component_required", order: 1100);
/// Image format (width, height, datatype).
///
/// Standard image formats like PNG or JPEG include this metadata in their headers,
/// allowing [archetypes.EncodedImage] to be self-describing. Depth images, however,
/// typically use headerless codecs, so this information must be provided explicitly.
format: rerun.components.ImageFormat ("attr.rerun.component_required", order: 1100);

Copy link
Author

Choose a reason for hiding this comment

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

done

bytes
}

fn encode_rvl(values: &[u16]) -> Vec<u8> {
Copy link
Member

Choose a reason for hiding this comment

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

Since you essentially implement a full encoder here as well, maybe it makes sense to just bundle the decoder + encoder up in a separate rust crate, so that it can be used by others as well?

Just a suggestion as I was surprised to see an encoder here as well 😅

Copy link
Author

Choose a reason for hiding this comment

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

refactored the parsing/encoding/decoding logic back to re_depth_compression crate

Copy link
Member

Choose a reason for hiding this comment

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

as far as I can see, this is 99% the same as the depth_images visualizer. The main difference is the component identifiers (e.g. DepthImage::meter() vs EncodedDepthImage::meter()).

Can we move the implementation to a unified function that takes the component identifiers?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants