dcm (mri image) parsing support#20
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
Adds native DICOM (.dcm) Part-10 decoding support to agno, including byte-level parsing, window/level rendering to RGB8, and integration into the auto-detecting loader pipeline.
Changes:
- Introduces a new
codec::dicommodule (parser + VOI math + renderer) behind adicomfeature (enabled by default). - Routes
.dcmfiles throughagno_image’s type detection + loader bridge, and adds integration/unit tests plus fixture-generation tooling. - Updates internal docs/rules to document the new codec and architecture, plus small unrelated tweaks (Just test invocation, PDF lexer test constant).
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/make_dicom_fixture.py | Adds a fixture generator for PHI-stripped DICOM + reference RGB render. |
| justfile | Adjusts cargo test invocation argument expansion. |
| CLAUDE.md | Documents the new dicom feature/codec support. |
| agno/tests/dicom_decode_tests.rs | Adds integration tests against a real DICOM fixture + loader routing + error-path test. |
| agno/src/exif/mod.rs | Treats DICOM as “no EXIF” (returns empty context). |
| agno/src/codec/pdf/lexer.rs | Updates a float parsing test constant. |
| agno/src/codec/mod.rs | Feature-gates and exposes the new codec::dicom module. |
| agno/src/codec/dicom/voi.rs | Adds modality LUT + linear window/level math with unit tests. |
| agno/src/codec/dicom/parse.rs | Adds a Part-10 parser with sequence skipping + metadata extraction and tests. |
| agno/src/codec/dicom/mod.rs | Adds public DICOM module entry point and documentation. |
| agno/src/codec/dicom/decode.rs | Adds pixel extraction + grayscale/RGB rendering to RGB8 with tests. |
| agno/src/agno_image/load/mod.rs | Adds and re-exports the DICOM loader bridge module. |
| agno/src/agno_image/load/load.rs | Extends format detection to recognize DICOM Part-10 and dispatch to loader. |
| agno/src/agno_image/load/dicom.rs | Implements load_dicom_from_bytes bridge (feature-gated) + tests. |
| agno/Cargo.toml | Adds dicom feature and enables it in default features. |
| .claude/rules/codec-guide.md | Documents DICOM decode API in the codec guide. |
| .claude/rules/architecture.md | Updates module map to include the new DICOM loader/codec. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address findings from a max-effort code review of the DICOM decoder: Correctness / robustness - Bound sequence nesting depth to prevent stack overflow on crafted deeply-nested undefined-length sequences (untrusted input). - Reject PALETTE COLOR / non-mono photometrics instead of silently rendering palette indices as grayscale. - Support multi-frame access: add decode_dicom_frame + load_dicom_frame_from_bytes and a DICOM arm in load_image_page so the page_count it reports is actually reachable. - Detect the DICM marker before leading-magic checks so a DICOM with a non-zero preamble (e.g. an embedded JPEG/TIFF preview) is not misrouted. - Support 16-bit RGB (mask to BitsStored, scale to 8-bit). - Reject non-finite WindowCenter/Width/Rescale (NaN/Inf) so a malformed value falls back to auto-window rather than producing an all-black frame. - Trim NUL padding (not just whitespace) when parsing DS/IS values. - Bound file-meta element lengths to the meta group so an overshooting element errors clearly instead of skipping the transfer syntax. - Fix make_dicom_fixture.py to mask/sign-extend from BitsStored and to auto-window when no VOI is present, matching the Rust decoder. Cleanup - Render MONOCHROME frames in a single streaming pass (no Vec<f64> intermediate; auto-window derived from raw sample extremes). - Share Part-10 test builders via codec/dicom/test_fixtures.rs and the PSNR helper via tests/common/mod.rs. - Remove the duplicated disabled-feature error stub; drop the dead window clamp and redundant default guards; add non-square coverage.
- lib_interface: check is_dicom() before the PDF/GIF magic dispatch in
load_image_page; a Part-10 preamble is arbitrary and may begin with
another format's magic, so the DICM marker must be authoritative
(mirrors detect_image_type()).
- decode: reject PlanarConfiguration values outside {0,1} instead of
silently treating any non-zero value as planar.
- decode: 8-bit RGB now masks to BitsStored and scales up to 8 bits,
matching the 16-bit and grayscale paths. Added tests for both.
- mod: replace intra-doc links into private modules with plain code
spans to avoid rustdoc private-link warnings.
- scripts/make_dicom_fixture.py: drop unused math import, require an
explicit source path with a usage message, and reword the misleading
"raw pixels are not identifying" comment.
- voi: clamp window output to [0,255] before the u8 cast so float roundoff at window endpoints can never land outside the byte range - load: rewind the file cursor after a failed DICOM probe so detect_image_type stays side-effect-free for non-DICOM inputs - decode: use checked arithmetic for frame size/offset math to reject crafted dimensions/frame counts instead of overflowing usize - scripts: drop the unused bits_allocated var from the fixture generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.