Standalone ground-side decoders for DISCO-2 / DIPP image records — the .bin
files pulled off the OBC.
Self-contained: nothing here references the DISCO2-module-template checkout.
The only external requirements are libopenjp2 and libprotobuf-c.
dipp-decode/
├── Makefile
├── dipp_decode.c full decoder → PNG (C, needs libopenjp2 + libprotobuf-c)
├── dipp_unpack.py metadata + payload extraction (Python 3, stdlib only)
└── vendor/
├── metadata.proto copied from DISCO2-module-template/protos/
├── metadata.pb-c.c generated protobuf-c bindings
├── metadata.pb-c.h
└── stb_image_write.h PNG writer
Produced by batch_util.c:append_result_image on the OBC, consumed by
csh/lib/csp_ippc/src/pipeline_slash.c:slash_csp_buffer_get:
repeat until EOF:
uint32 meta_size little-endian
bytes metadata protobuf Metadata (vendor/metadata.proto)
bytes payload meta.size bytes of image data
A single downloaded record holds one image, but both tools loop, so a concatenated batch works too.
The payload encoding depends on the module that produced it. jpeg2000_module.c
uses OPJ_CODEC_J2K, so the payload is a raw J2K codestream (magic
FF 4F FF 51), not a JP2 container, and it tags the metadata with enc = "j2k".
Note: csh's own
-s/--save_pngflag only understandsenc == "jxl"(pipeline_slash.c:639). Point it at a j2k payload and it renders raw garbage. That is why these tools exist.
make
./dipp_decode image.bin # metadata + decoded PNG
./dipp_decode image.bin -o out # write outputs to out/
./dipp_decode image.bin -o out -r # also keep the raw .j2k
./dipp_decode image.bin -m # metadata only, writes nothing- Decodes raw J2K and JP2 payloads.
- Always writes an 8-bit
.png. For >8-bit images it also writes a 16-bit.pnm, since PNG output here is 8-bit only and you would otherwise lose the low bits of a 12- or 16-bit sensor frame. - Payloads that are already PNG/JPEG/WebP/JXL are written out verbatim.
- A payload with no recognisable magic is treated as interleaved raw samples if
its length matches
width * height * channels * bytes_per_sample; otherwise it is dumped as.rawfor inspection. - Exits non-zero if any record fails.
If autodetection picks the wrong openjpeg:
make OPJ_INC=/usr/include/openjpeg-2.5 OPJ_LIB=/usr/lib/x86_64-linux-gnuThe Makefile ignores the ambient CC/CFLAGS and does not use pkg-config on
purpose — after sourcing the Yocto SDK environment both point into the aarch64
cross sysroot, which would quietly build a binary that cannot run on the
workstation.
Pure stdlib, including a small protobuf wire-format reader. Runs on any machine with Python 3 and no build environment.
./dipp_unpack.py image.bin # metadata + extract payloads
./dipp_unpack.py image.bin -m # metadata onlyIt does not decode JPEG 2000 — it extracts the .j2k so you can open it in any
viewer, or hand it to opj_decompress:
./dipp_unpack.py image.bin -o out
opj_decompress -i out/image_img0.j2k -o out/image_img0.png=== image 0 (metadata 43 bytes) ===
size : 282 bytes (payload)
dimensions : 64 x 48
channels : 3
bits_pixel : 8
timestamp : 1753100000
obid : 42
camera : wide-angle
items : 1
enc = "j2k" (string)
payload : 282 bytes, JPEG 2000 raw codestream
decoded: 64x48, 3 component(s), 8 bit/sample
wrote out/image_img0.png
When something looks wrong, check in this order:
payload : ... JPEG 2000 raw codestream— the module ran and produced real j2k. If it saysuncompressed / unknown, the pipeline passed the input through without compressing.enc = "j2k"— confirmsadd_custom_metadata_stringfired.sizevsdimensions— compression ratio. Ifsize≈width × height × channels, nothing compressed.bits_pixel— if this reads 16 but the sensor is really 8-bit, the encoder read past the end of the input buffer and the image will look like noise.opj_read_header failed/opj_decode failed— the codestream itself is malformed. That points at the encoder settings on the OBC, not the download.
Only needed if metadata.proto changes upstream:
protoc-c --c_out=. --proto_path=vendor vendor/metadata.proto && mv metadata.pb-c.* vendor/- 8-bit RGB lossless payload decodes bit-exact against the source pixels.
- 12-bit grayscale lossy payload decodes, with full depth preserved in the
16-bit
.pnm. - Multi-record files, uncompressed payloads, truncated files, and random junk all handled without crashing; non-zero exit on failure.
- Both tools extract byte-identical payloads from the same input.