XPolicyLab에서 카메라 색을 다룰 때는 입력이 저장된 JPEG 바이트인지, 모델 서버가 이미 풀어 놓은 RGB 배열인지부터 구분해야 합니다. 저장 영상에는 과거 형식과 새 형식이 함께 존재하며, 공식 디코더가 두 형식을 구별해 RGB로 맞춥니다. 반면 model.py의 관측 입력은 이미 RGB 배열이므로 모델 쪽에서는 그 배열을 받아 처리합니다.
이 글은 2026년 10월 15일(UTC) 확인한 공식 문서의 데이터 형식과 어댑터 계약을 분석한 글입니다. 디코더나 로봇 정책을 직접 실행한 결과는 포함하지 않습니다.
JPEG라는 확장자만으로 색 순서를 알 수 없는 이유
RoboDojo 사용 문서는 환경·작업을 다루는 쪽과 정책을 연결하는 XPolicyLab의 역할을 나눕니다. 이 연결에서 영상은 저장 파일, 변환기, 모델 입력을 거치는데, 겉으로 같은 JPEG라도 작성 경로에 따라 색 해석이 달라집니다.
XPolicyLab의 표준 데이터 형식 문서에 따르면 과거 데이터는 RGB 배열을 cv2.imencode에 바로 넣었습니다. OpenCV 인코더는 입력 채널을 BGR로 해석하므로, 만들어진 바이트를 표준 JPEG 디코더로 읽으면 빨강과 파랑이 뒤바뀝니다. 이 과거 경로에서는 cv2.imdecode가 돌려주는 채널 순서가 다시 맞아떨어졌습니다.
새 encode_image_bit 경로는 RGB를 먼저 BGR로 바꾼 다음 인코딩합니다. JPEG 규약에 맞춰 색을 저장하므로 일반 디코더에서도 원래 색으로 보입니다. 두 경로의 차이는 다음과 같습니다.
| 저장 형식 | 인코딩에 들어가기 전 처리 | 일반 JPEG 디코더에서의 색 |
|---|---|---|
| 과거 형식 | RGB 배열을 BGR 입력으로 해석하는 인코더에 바로 전달 | 빨강·파랑이 뒤바뀜 |
| 새 표준 형식 | RGB를 BGR로 바꾼 뒤 인코딩 | 원래 색으로 해석 |
따라서 한 파일에서 잘 보이는 디코딩 방법이 다른 시기의 파일에서도 같은 뜻을 가진다고 볼 수는 없습니다. 중요한 구분은 파일 확장자가 아니라 그 바이트를 만든 경로입니다.
XPL-RGB1 표식은 각 이미지 버퍼에 붙는다
새 인코더는 JPEG의 COM 구간에 XPL-RGB1이라는 표식을 넣습니다. 데이터셋 바깥의 파일 속성이 아니라 이미지 버퍼 안에 넣기 때문에, 한 프레임만 떼어 옮겨도 표식이 따라갑니다. 일반 JPEG 디코더는 이 주석 구간을 건너뛰면서 영상을 읽습니다.
공식 문서는 과거 데이터를 새 형식으로 일괄 재인코딩하지 않는다고 설명합니다. JPEG를 다시 인코딩하며 채널을 바꾸는 방식은 원본을 무손실로 유지하는 변환이 아니기 때문입니다. 그 결과 과거 형식과 새 형식이 한 학습 실행에 섞일 수 있습니다.
XPolicyLab.utils.process_data의 decode_image_bit는 이 차이를 처리하고 RGB를 반환하는 공통 진입점입니다. 오프라인 데이터 변환과 학습에서는 저장 이미지 비트를 이 함수로 읽고, 새로 저장할 때는 짝인 encode_image_bit를 사용하도록 문서가 정하고 있습니다. 디코더가 돌려준 RGB 뒤에 채널 뒤집기를 한 번 더 붙이면 오히려 결과가 바뀝니다.
빨간 물체가 있는 두 에피소드를 섞는 가상 예
빨간 블록을 집는 과거 에피소드 A와, 같은 색의 블록을 담은 새 에피소드 B가 있다고 가정하겠습니다.
표준 JPEG 디코더만 쓰는 로더라면 A의 블록은 파란 쪽으로 해석되고 B는 빨간 쪽으로 보입니다. 여기서 모든 영상의 빨강·파랑을 무조건 바꾸면 A는 맞아지지만 B가 바뀝니다. 특정 색으로 집을 물체를 지정한 지시가 있다면, 같은 전처리 코드가 두 에피소드의 지시와 영상 사이 관계를 서로 다르게 바꾸는 셈입니다.
표식에 따라 처리하는 공식 디코더를 통과시키면 두 저장 경로가 모두 RGB라는 같은 약속으로 모입니다. 비교 대상은 이 채널의 의미입니다. JPEG 자체의 압축 때문에 각 픽셀의 숫자는 원래 배열과 조금 달라질 수 있으므로, 예시의 빨간 블록을 특정 RGB 정수값의 완전 복원으로 해석할 필요는 없습니다.
모델 어댑터까지 들어오면 디코딩은 끝나 있다
XPolicyLab 서버는 update_obs 또는 update_obs_batch를 부르기 전에 카메라 색 영상을 디코딩합니다. model.py에서 받는 obs["vision"][카메라]["color"]는 (H, W, 3) RGB 이미지 배열입니다. 모델별 크기 조절이나 정규화를 붙이는 지점과, 압축 바이트를 이미지로 푸는 지점이 여기서 나뉩니다.
저장된 궤적 쪽은 이름과 범위도 다릅니다. vision/cam_head/colors는 시간축이 있는 (T, H, W, 3) RGB 배열이거나 인코딩된 스트림일 수 있습니다. 문서의 변환 헬퍼는 한 프레임과 시퀀스, 이미 변환된 값을 처리하도록 설명되어 있습니다. 저장 데이터의 colors를 읽는 로더와 실시간 관측의 color를 받는 어댑터를 같은 단계로 취급하면 디코딩 책임이 중복되기 쉽습니다.
저장된 이미지 비트 → decode_image_bit → RGB 배열 → 모델별 전처리
실시간 관측 → 서버의 디코딩 → update_obs의 RGB 배열 → 모델별 전처리
새 이미지 저장: RGB 배열 → encode_image_bit → 표식이 있는 JPEG
어댑터를 읽을 때는 데이터가 이 세 경로 중 어디에 있는지 표시하면 됩니다. 이미지 쪽의 공통 RGB 약속을 세운 뒤에는 행동 라벨도 모델이 기대하는 의미로 맞춰야 합니다. 그 다음 단계는 OpenVLA의 Bridge·RT-1 그리퍼 행동 변환에서 입력 데이터의 표현을 바꾸는 다른 사례로 이어집니다.