Environment: - Container: nvcr.io/nvidia/nre/nre-tools-ga:latest - Host OS: Ubuntu 24.04 LTS - GPU: NVIDIA RTX 6000 Ada Generation (48GB VRAM) Dri

Environment:

Problem:
I converted my own sensor data (5 cameras + 1 LiDAR) to NCore V4 format.
The zarr store contains: cameras, intrinsics, lidars, poses - but NO masks component.

What we tried:

  1. --segmentation-backend=none → still fails with “MasksComponent group ‘default’ not found”
  2. We reverse-engineered the MasksComponent code from inside the container and found the expected structure:
    masks/default/cameras/<camera_id>/
    attrs: {“mask_names”: [“segmentation”]}
    <frame_name>: PNG-encoded bytes
  3. We wrote masks using MasksComponent.Writer from inside the container with Python 3.11
  4. But ncore-aux-data still cannot find the masks

Root cause suspect: Zarr version incompatibility between host (zarr 3.x) and container (zarr 2.x).
The masks written by the container’s own MasksComponent.Writer are also not found.

Questions:

  1. Is --segmentation-backend=none supposed to skip the masks check completely?
  2. What is the exact expected zarr format/version for the masks component?
  3. Is there a way to run ncore-aux-data without any masks component at all?
  4. Can you provide a minimal working example for custom NCore V4 data without masks?

Thank you for the post. I will ask one of the NuRec engineers to get back to you.

Q1: Is --segmentation-backend=none supposed to skip the masks check completely?

No — these are two unrelated things.
--segmentation-backend=none controls whether semantic segmentation is generated during aux-data processing. Your error happens earlier, at dataset loading, when SequenceLoaderV4 is constructed. The tool always passes masks_component_group_name="default" to the loader, and if that component group doesn’t exist in your zarr store, it fails before any segmentation logic even runs.
The SequenceLoaderV4 public API ( NCore — NCore ) actually supports masks_component_group_name=None to skip masks entirely — but ncore-aux-data hardcodes the default to "default" with no way to pass None through the CLI.
Separately, note that --lidar-seg-camvis is enabled by default and independently requires semantic segmentation data to already exist when --segmentation-backend=none. You’ll want --no-lidar-seg-camvis as well.

Q2: What is the exact expected zarr format/version for the masks component?

NCore uses zarr 2.x, but the zarr version is not your root cause.
The masks component must be registered through SequenceComponentGroupsWriter.register_component_writer() during store creation. This call both writes the data and registers the component in the V4 component group index that SequenceComponentGroupsReader uses to discover components at read time. Manually writing zarr groups at the expected paths — even with the correct zarr version — won’t work because the reader finds components through this index, not by scanning paths. This is why your MasksComponent.Writer approach from inside the container didn’t work: the component was written but never registered in the index.

Q3: Is there a way to run ncore-aux-data without any masks component at all?

Not currently. The underlying SequenceLoaderV4 API supports it (masks_component_group_name accepts None, and the public docs ( NCore — NCore ) explicitly mark MasksComponent as optional), but ncore-aux-data provides no way to pass None for --masks-component-group.
Workaround: Register an empty masks component during your conversion.

Q4: Minimal working example for custom NCore V4 data without masks

Using the public API (ncore.data.v4):

from upath import UPath
from ncore.data.v4 import (
    SequenceComponentGroupsWriter,
    CameraSensorComponent,
    LidarSensorComponent,
    PosesComponent,
    IntrinsicsComponent,
    MasksComponent,
    CuboidsComponent,
)
from ncore.impl.common.transformations import HalfClosedInterval

store_writer = SequenceComponentGroupsWriter(
    output_dir_path=UPath("/path/to/output"),
    store_base_name="my_sequence",
    sequence_id="my_sequence",
    sequence_timestamp_interval_us=HalfClosedInterval(start_us, end_us),
    store_type="itar",
    generic_meta_data={},
)

# Poses
poses_writer = store_writer.register_component_writer(
    PosesComponent.Writer,
    component_instance_name="default",
    group_name="default",
)
poses_writer.store_dynamic_pose(
    source_frame_id="rig",
    target_frame_id="world",
    poses=T_rig_worlds,           # float32 [N, 4, 4]
    timestamps_us=pose_times_us,  # uint64 [N]
)

# Intrinsics
intrinsics_writer = store_writer.register_component_writer(
    IntrinsicsComponent.Writer,
    component_instance_name="default",
    group_name="default",
)

# Cameras
for camera_id in camera_ids:
    intrinsics_writer.store_camera_intrinsics(camera_id, camera_model_params)
    camera_writer = store_writer.register_component_writer(
        CameraSensorComponent.Writer,
        component_instance_name=camera_id,
    )
    for frame in frames:
        camera_writer.store_frame(
            image_binary_data=frame.jpeg_bytes,
            image_format="jpeg",
            frame_timestamps_us=frame.timestamps_us,
            generic_data={},
            generic_meta_data={},
        )

# LiDARs
for lidar_id in lidar_ids:
    lidar_writer = store_writer.register_component_writer(
        LidarSensorComponent.Writer,
        component_instance_name=lidar_id,
    )
    # ... store lidar frames ...

# Masks — required by ncore-aux-data, but can be empty
masks_writer = store_writer.register_component_writer(
    MasksComponent.Writer,
    component_instance_name="default",
    group_name="default",
)
for camera_id in camera_ids:
    masks_writer.store_camera_masks(camera_id=camera_id, mask_images={})

# Cuboids — required by ncore-aux-data, but can be empty
store_writer.register_component_writer(
    CuboidsComponent.Writer,
    "default",
    "default",
).store_observations([])

# Finalize — consolidates metadata and seals the .zarr.itar
store_writer.finalize()