Skip to content

Baseline support for deflate transfer syntaxes with dataset adapter rewrite#438

Merged
Enet4 merged 29 commits intoEnet4:masterfrom
naterichman:deflated-ts
Jul 27, 2025
Merged

Baseline support for deflate transfer syntaxes with dataset adapter rewrite#438
Enet4 merged 29 commits intoEnet4:masterfrom
naterichman:deflated-ts

Conversation

@naterichman
Copy link
Copy Markdown
Contributor

No description provided.

@Enet4 Enet4 added A-lib Area: library C-object Crate: dicom-object C-pixeldata Crate: dicom-pixeldata C-transfer-syntax Crate: dicom-transfer-syntax-registry labels Mar 22, 2024
@Enet4 Enet4 added the breaking change Hint that this may require a major version bump on release label Apr 14, 2024
@Enet4 Enet4 mentioned this pull request Dec 7, 2024
@Enet4
Copy link
Copy Markdown
Owner

Enet4 commented Dec 8, 2024

I had another try at this. Last time we talked about deflate TS support, the main problem was that the data RW adapter would not allow us to bind the lifetime of the adapted reader/writer to the lifetime of the input reader/writer. With the current signature, this is simply impossible.

So we had to change the trait DataRWAdapter. One way that we can do in modern Rust thanks to GATs is something the likes of this:

pub trait DataRWAdapter {
    type Reader<'r, R: 'r>: Read
        where Self: 'r;
    type Writer<'w, W: 'w>: Write
        where Self: 'w;

    /// Adapt a byte reader.
    fn adapt_reader<R>(&self, reader: R) -> Self::Reader<R>;

    /// Adapt a byte writer.
    fn adapt_reader<W>(&self, writer: W) -> Self::Writer<W>;
}

As delightful as this trait is, it is not dyn-compatible (the new nomenclature for what was previously known as object-safe), so we could not have a registry for any adapter of this kind.

Ultimately, we have to go for a fully dynamic approach with an added lifetime parameter at each method.

pub trait DataRWAdapter {
    /// Adapt a byte reader.
    fn adapt_reader<'r>(&self, reader: Box<dyn Read + 'r>) -> Box<dyn Read + 'r>;

    /// Adapt a byte writer.
    fn adapt_writer<'w>(&self, writer: Box<dyn Write + 'w>) -> Box<dyn Write + 'w>;
}

After propagating the changes to the rest of the code, we thus bring support for deflated DICOM data sets in dicom-dump, dicom-toimage, and many other tools.


This is not yet ready though.

  • dicom-transcode needs a few more tweaks in order to enable reading and writing deflated DICOM files. I can work on this soon-ish. Done
  • Two of the new tests need to be changed. A round trip from deflated DICOM data and back to deflated DICOM data does not guarantee a file with the same size, since it could be done using a different deflate encoder implementation with different compression parameters.

@Enet4 Enet4 added this to the DICOM-rs 0.9 milestone Mar 11, 2025
@Enet4
Copy link
Copy Markdown
Owner

Enet4 commented Apr 18, 2025

Good news and bad news. The good news is that this pull request is ready. The bad news is that I could not find any issues in the latest revision so far. Maybe you can help cracking down any eventual bugs? 😆

@Enet4 Enet4 marked this pull request as ready for review April 18, 2025 09:39
Copy link
Copy Markdown
Contributor Author

@naterichman naterichman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, just a couple comments. Sorry for the long delay on this, I've been super busy!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remember what the behavior of this was, but I feel like we should make a Dataset encoding for this that panics with a message about adding the deflate feature.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit torn on that one. Unless I overlooked something, the way to do that is to create and declare a stub data set implementation which raises an error whenever it is called. Semantically, the system would be claiming to support deflated explicit VR little endian at Codec level (on checking that it matches Codec::Dataset(Some(_))), but then still fail at run-time. So any application depending on this check in an automatic discovery of supported transfer syntaxes would be at risk of inadvertently including the stub.


OK, there is one other thing that we can try, which is to adapt the error message in places where support for certain transfer syntaxes were requested, and suggest enabling features there. Need to investigate.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extended the error types ReadError and WriteError in dicom_object so that they can provide a suggestion to enable a Cargo feature in order to support that transfer syntax. For example:

❯ cargo run -p dicom-toimage --no-default-features --bin dicom-toimage -- ../dicom-test-files/data/pydicom/image_dfl.dcm

025-06-21T18:45:02.927669Z ERROR dicom_toimage: could not read DICOM file ../dicom-test-files/data/pydicom/image_dfl.dcm

Caused by this error:
  1: Unsupported reading for transfer syntax `1.2.840.10008.1.2.1.99` (Deflated Explicit VR Little Endian, try enabling feature `dicom-transfer-syntax-registry/deflate`)

@Enet4 Enet4 changed the title First crack at adding dataset read adapter in DS read for deflate Baseline support for deflate transfer syntaxes with dataset adapter rewrite Jun 19, 2025
Copy link
Copy Markdown
Owner

@Enet4 Enet4 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback!

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit torn on that one. Unless I overlooked something, the way to do that is to create and declare a stub data set implementation which raises an error whenever it is called. Semantically, the system would be claiming to support deflated explicit VR little endian at Codec level (on checking that it matches Codec::Dataset(Some(_))), but then still fail at run-time. So any application depending on this check in an automatic discovery of supported transfer syntaxes would be at risk of inadvertently including the stub.


OK, there is one other thing that we can try, which is to adapt the error message in places where support for certain transfer syntaxes were requested, and suggest enabling features there. Need to investigate.

@rforsyth
Copy link
Copy Markdown
Contributor

Thanks for adding this feature. We would like to be able to use it. Are there any blockers to merging the PR Eduardo?

@Enet4

@Enet4
Copy link
Copy Markdown
Owner

Enet4 commented Jul 16, 2025

Hello @rforsyth! The short-term plan is to gather the contributions without breaking changes into v0.8.2, then we'll immediately start joining all pending breaking changes for v0.9.0. I hope to get to this soon, as I understand it's been a while since the last release.

naterichman and others added 9 commits July 27, 2025 13:05
* Note, not all paths lead to writing, for example using
  `InMemDicomObject.read_dataset_with_dict` would bypass deflate.
* No support for writing right now
- make it fully dynamic and pass a lifetime from input to output
- change the rest of the code to fit DataRWAdapter
- revert 'static lifetime constraints
- remove unused file
- remove use of dicom_pixeldata
- inspect dicom object contents
- JPIP Referenced Deflate
- JPIP HTJ2K Referenced Deflate
Enet4 added 20 commits July 27, 2025 13:05
- check for differences in encapsulated data from source TS to target TS
  instead of whether they are codec-free
- only run it if JPIP Referenced Deflate is not fully supported yet
- split unsupported transfer syntax into
  unrecognized (not found in registry)
  and unsupported (requires dataset adapter impl)
- split unsupported TS error further to show suggested feature name
  (in case of deflated TSes)
- move common logic into read_parts_with_all_options_impl
- add SOP attribute inference to from_reader
- move detect_preamble function
- do not try to read it if the transfer syntax requires a dataset adapter
  that the TS specified does not have
- the features are not needed for baseline dicom-object capabilities
- remove deflate as a default feature
- propagate deflate feature to dicom-object and dicom-pixeldata
- [pixeldata] add deflate as a capability in "native"
- [ts-registry][pixeldata] extend and reorganize root documentation
- add support for other deflated TSes
@Enet4
Copy link
Copy Markdown
Owner

Enet4 commented Jul 27, 2025

We're ready to merge this. Thanks again @naterichman. 💪

@Enet4 Enet4 merged commit 1855561 into Enet4:master Jul 27, 2025
5 checks passed
@Enet4 Enet4 linked an issue Dec 17, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-lib Area: library breaking change Hint that this may require a major version bump on release C-object Crate: dicom-object C-pixeldata Crate: dicom-pixeldata C-transfer-syntax Crate: dicom-transfer-syntax-registry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deflated Explicit VR Little Endian Case - "Could not read data set token"

3 participants