Skip to content

robodojo and robotwin dataloaders decode JPEG frames with opposite channel conventions #13

Description

@rakhimovv

The two HDF5 dataloaders decode their JPEG camera buffers with opposite channel conventions. robotwin.py uses cv2.imdecode and documents exactly why; robodojo.py uses PIL and says nothing about it. I could not settle which is right for RoboDojo from the repo alone, so this is a question rather than a bug report — but the asymmetry looked worth raising.

The two decoders

openwam/dataloader/robotwin.py:624-634 (line numbers on main):

def _decode_jpeg(self, jpeg_bytes) -> Image.Image:
    """Decode JPEG bytes from HDF5 to a PIL RGB image.

    RoboTwin encodes frames by passing RGB arrays directly to
    ``cv2.imencode`` (which expects BGR), so R and B channels are
    swapped inside the JPEG. Using ``cv2.imdecode`` reverses this
    swap, giving back the original RGB order — no further
    conversion needed.
    """
    arr = cv2.imdecode(np.frombuffer(bytes(jpeg_bytes), np.uint8), cv2.IMREAD_COLOR)

openwam/dataloader/robodojo.py:359-365:

def _decode_jpeg(value: Any, *, source: str) -> Image.Image:
    ...
    with Image.open(io.BytesIO(encoded)) as image:
        return image.convert("RGB").copy()

PIL.Image.open(...).convert("RGB") returns channels in stored order; cv2.imdecode returns BGR, which is what undoes a cv2.imencode of an RGB array. The two paths therefore disagree by an R/B swap whenever both datasets were written by the same kind of encoder.

Why it may matter

git grep -nE 'COLOR_BGR2RGB|COLOR_RGB2BGR|\[\.\.\., ::-1\]|\[:, :, ::-1\]' -- openwam/dataloader/ returns nothing on main, so neither loader corrects afterwards, and the deploy path does no conversion either — openwam/deploy/obs_preprocess.py:142,148 decodes incoming frames with Image.open(...).convert("RGB") and hands them on. So a model trained through robodojo sees whatever PIL yields, and at eval sees whatever the client encoded, with nothing in between to reconcile them.

If RoboDojo's vision/<camera>/colors buffers were written the way robotwin.py describes for RoboTwin — an RGB array passed to cv2.imencode — then robodojo.py trains on channel-swapped frames and evaluates on correct ones.

What I could not determine

Whether that "if" holds. OpenWAM ships no RoboDojo encoder — grep -rn 'imencode|imwrite|encode_jpeg' --include='*.py' openwam/ scripts/ benchmarks/ finds only robotwin.py's comment, benchmarks/ebench/mock_genmanip_server.py (which encodes with turbojpeg TJPF_RGB, i.e. RGB-correct), and one cv.imwrite in the RoboTwin benchmark client that converts to BGR first. So the RoboDojo recorder is external to this repo and I have no RoboDojo trajectories here to test against.

If the RoboDojo buffers are RGB-correct, robodojo.py is right and the only thing missing is a comment saying so — which would still be worth having next to a sibling that documents the opposite convention.

Anyone with a trajectory file can settle it in a few lines:

import io, numpy as np, cv2
from PIL import Image

buf = ...  # one entry from vision/<camera>/colors
pil = np.asarray(Image.open(io.BytesIO(buf)).convert("RGB"))
cv  = cv2.imdecode(np.frombuffer(buf, np.uint8), cv2.IMREAD_COLOR)
print(pil[0, 0], cv[0, 0])   # equal only if the frame is grey at that pixel

then check which ordering matches the scene — a wooden table or a red block makes it obvious by eye.

Related

The same decode is reported downstream against the XPolicyLab adapter in XPolicyLab/XPolicyLab#116, where the eval side is known to be RGB. The code there is a vendored copy of this loader, and robodojo.py's own module docstring notes that the Isaac eval runtime keeps pinned copies of these contracts — so if the swap is real, it is real in both places and worth fixing at the source.

Verified on Python 3.10.20, torch 2.7.1+cu128, at main (f6bae3e). Happy to send a PR — either the cv2.imdecode treatment robotwin.py already uses, or just the clarifying comment — once someone with the data says which way it goes.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions