Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Add support for High-Throughput JPEG 2000 #897

Open
dazzag24 opened this issue Dec 9, 2024 · 19 comments
Open

Feature request: Add support for High-Throughput JPEG 2000 #897

dazzag24 opened this issue Dec 9, 2024 · 19 comments

Comments

@dazzag24
Copy link

dazzag24 commented Dec 9, 2024

The HTJ2K codec is an order of magnitude faster than JPEG2000, and at least twice as fast as all other DICOM transfer syntaxes. With HealthImaging, applications can leverage HTJ2K with single instruction multiple data (SIMD) instructions, for exceptional image decoding performance. Further, modern browsers can utilize Web Assembly SIMD (WASM-SIMD) to bring industry leading performance to zero-footprint web viewers.

ref: https://aws.amazon.com/blogs/industries/introducing-aws-healthimaging/

HTJ2K is the abbreviation for High-Throughput JPEG 2000, which is defined in Part 15 of the JPEG2000 standard (ISO/IEC 15444-15:2019). HTJ2K retains the advanced features of JPEG2000 such as resolution scalability, precincts, tiling, high bit depth, multiple channels, and color space support.

During import, AWS HealthImaging encodes all image frames in HTJ2K lossless format to deliver consistently fast image display and universal access to HTJ2K’s advanced features. Depending on your programming language, we recommend the following decoding libraries for image frames (pixel data).

OpenJPH

OpenJPEG

openjphpy

pylibjpeg-openjpeg

Kakadu Software

ref: https://docs.aws.amazon.com/healthimaging/latest/devguide/reference-htj2k.html

Is this something that could make sense for dcm2niix to support?

Thanks

@neurolabusc
Copy link
Collaborator

@dazzag24 dcm2niix uses OpenJPEG to decode jpeg2000, and OpenJPEG is one of the decoding libraries that supports HTJ2K. Therefore, you should have this support if you compile dcm2niix with OpenJPEG 2.5.0 or later.

@dazzag24
Copy link
Author

dazzag24 commented Dec 10, 2024

Does dcm2niix need to be made aware of the new transport syntaxes? Looks that way.

❯  ./dcm2niix -v
Chris Rorden's dcm2niiX version v1.0.20241208  (JP2:OpenJPEG) (JP-LS:CharLS) Clang12.0.0 ARM (64-bit MacOS)
v1.0.20241208

❯ ./dcm2niix -o ~/Downloads/converted_decomps5 -z y -f %j_%k ~/Desktop/converted_decomps5
Chris Rorden's dcm2niiX version v1.0.20241208  (JP2:OpenJPEG) (JP-LS:CharLS) Clang12.0.0 ARM (64-bit MacOS)
Found 473 DICOM file(s)
Warning: Unsupported transfer syntax '1.2.840.10008.1.2.4.201' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)
Warning: Unsupported transfer syntax '1.2.840.10008.1.2.4.201' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)
Warning: Unsupported transfer syntax '1.2.840.10008.1.2.4.201' (see www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage)

I downloaded the most recent version: https://github.com/rordenlab/dcm2niix/releases

@neurolabusc
Copy link
Collaborator

I am unable to replicate. Can you share a sample of this transfer syntax with my institutional email, or describe how a DICOM image can be converted to this transfer syntax. It does not seem like gdcmconv 3.0.24 that uses OpenJPEG 2.5.3 supports this yet.

@dazzag24
Copy link
Author

There are a couple of examples here:
https://github.com/pydicom/pydicom-data/tree/master/data_store/data
see the files beginning "HTJ2K*" which have the following transfer syntaxes

1.2.840.10008.1.2.4.201
1.2.840.10008.1.2.4.203

See https://github.com/pydicom/pydicom/blob/153a3a9afeb35165ff175ebfbe21eca1a93014fc/src/pydicom/uid.py#L335 for a mapping to the human readable name.

I can send you a full series that uses 1.2.840.10008.1.2.4.201 but its a 140Mb zip so possibly to large to email.

@dazzag24
Copy link
Author

Hi, Did you receive the example series that I emailed?
Thanks

@dazzag24
Copy link
Author

dazzag24 commented Dec 12, 2024

I became aware of another set of example image today that are being used by the ITK team

See https://www.aliza-dicom-viewer.com/download/datasets and the link to "Color files, 1920 x 1080 px (update 2024.01.08, added HTJ2K)"

@neurolabusc
Copy link
Collaborator

@dazzag24 why don't you try out the latest commit to the development branch (v1.0.20241211). You have to compile with the OpenJPEG. The easiest way to do this is with the cmake superbuild:

git clone --branch development https://github.com/rordenlab/dcm2niix.git
cd dcm2niix
mkdir build && cd build
cmake -DZLIB_IMPLEMENTATION=Cloudflare -DUSE_JPEGLS=ON -DUSE_OPENJPEG=ON ..
make

I also added a faster way to build, but you will have to tune the /console/makefile to match the location of your JPEG2000 library:

git clone --branch development https://github.com/rordenlab/dcm2niix.git
cd dcm2niix\console
make jp2

@dazzag24
Copy link
Author

Hi,

Apologies for the delay. I finally got around to doing some testing on this. As you know I found an issue with the converted NIfTI files being much smaller but wanted to be sure that it was not a problem with how my DICOM compression code was producing the HTJ2K DICOMs I was using in testing. I am now confident that this not the case.

Using the test files from Aliza (see above) and from pydicom I also see issues where the output NIfTI file is corrupt.

Using the HTJ2KLossless_08_RGB.dcm file from the pydicom test data

~/Downloads/dcm2niix -o ~/Desktop/niftis -z y -f %j_%k -s y HTJ2KLossless_08_RGB.dcm
Chris Rorden's dcm2niiX version v1.0.20241211  (JP2:OpenJPEG) (JP-LS:CharLS) Clang12.0.0 ARM (64-bit MacOS)
Warning: Instance number (0020,0013) not found: HTJ2KLossless_08_RGB.dcm
Warning: Patient Position (0018,5100) not specified (issue 642).
Warning: Unable to determine manufacturer (0008,0070), so conversion is not tuned for vendor.
Convert 1 DICOM as /Users/xxxxx/Desktop/niftis/_ (640x480x1x1)
Conversion required 0.052217 seconds (0.048659 for core code).

The resulting NIfTI looks like this in 3D Slicer:

image

Also with HTJ2K-YBR_ICT.dcm file from the pydicom test data

~/Downloads/dcm2niix -o ~/Desktop/niftis -z y -f %j_%k -s y Aliza-DICOM-viewer-HTJ2k/HTJ2K-YBR_ICT.dcm
Chris Rorden's dcm2niiX version v1.0.20241211  (JP2:OpenJPEG) (JP-LS:CharLS) Clang12.0.0 ARM (64-bit MacOS)
Warning: Patient Position (0018,5100) not specified (issue 642).
Warning: Unable to determine manufacturer (0008,0070), so conversion is not tuned for vendor.
Convert 1 DICOM as /Users/xxxxx/Desktop/niftis/1.2.826.0.1.3680043.9.4245.6602037944901915650477181237096105672_1.2.826.0.1.3680043.9.4245.7964578538842334580361701724537168429 (1920x1080x1x1)
Conversion required 0.271147 seconds (0.249147 for core code).

The resulting NIfTI looks like this in 3D Slicer:

image

NIfTIs from my own test dataset I see output like this:

image

I'm fairly confident that my HTJ2K DICOMs are valid as I have code that compares the pixel array from the original DICOMs with the decompressed pixel arrays and they seem valid (e,g I have test code that takes an original DICOM, compresses it and then opens the compressed file using pydicom accesses the pixel_array object).

Furthermore my HTJ2K compressed DICOMs are correctly rendered in both
https://unpkg.com/[email protected]/build/index.html
and
https://chafey.github.io/openjphjs/test/browser/index.html

Please let me know if you want anymore info.

Thanks

@neurolabusc
Copy link
Collaborator

@dazzag24 you are correct, the current dcm2niix cmake uses OpenJPEG 2.1 while high throughput (HTJ2K) decoding requires OpenJPEG 2.5.0 or later. You can use the make jp2 from the console folder to see that dcm2niix works correctly with these images, but this requires you to manually install a recent OpenJPEG version.

@ningfei would it be possible to update your cmake library for the latest stable OpenJPEG?

@dazzag24
Copy link
Author

dazzag24 commented Dec 31, 2024

Using an exe compiled using make jp2 yields sensible looking results upon initial inspection:

./dcm2niix -o ~/Desktop/niftis -z y -f %j_%k -s y Aliza-DICOM-viewer-HTJ2k/HTJ2K-YBR_ICT.dcm
Chris Rorden's dcm2niiX version v1.0.20241211 (JP2:OpenJPEG) Clang16.0.0 ARM (64-bit MacOS)
Warning: Patient Position (0018,5100) not specified (issue 642).
Warning: Unable to determine manufacturer (0008,0070), so conversion is not tuned for vendor.
Convert 1 DICOM as /Users/xxxxx/Desktop/niftis/1.2.826.0.1.3680043.9.4245.6602037944901915650477181237096105672_1.2.826.0.1.3680043.9.4245.7964578538842334580361701724537168429 (1920x1080x1x1)
Conversion required 0.293713 seconds (0.285138 for core code).

image

@ningfei
Copy link
Collaborator

ningfei commented Dec 31, 2024

will have a look when I find the time.

@dazzag24
Copy link
Author

dazzag24 commented Dec 31, 2024

For now I have added some extra steps to my Dockerfile that downloads and compiles
https://github.com/uclouvain/openjpeg/archive/refs/tags/v2.5.3.tar.gz
locally. I've had to add a slightly hacky sed replace command to replace the hardcoded /opt/homebrew/Cellar/openjpeg/2.5.3 in the console makefile, but it works.

Thanks

@dazzag24
Copy link
Author

Quick question. I normally compile with
cmake -DBATCH_VERSION=ON -DZLIB_IMPLEMENTATION=Cloudflare -DUSE_JPEGLS=ON

By using make jp2 in the console directory am I missing out on the zlib optimisation and JPEGLS support?

@neurolabusc
Copy link
Collaborator

I have added a commit to the development branch that provides arguments to make can combine JPEG2000 and JPEG-LS support. DICOM supports different types of JPEG. dcm2niix always includes inbuilt support for lossless JPEG, requires CharLS for JPEG-lossless, and requires OpenJPEG for JPEG2000.

command features
make miniz compressor
make jp2 JP2000 support
JPEGLS=1 make JP-LS support
JPEGLS=1 make jp2 JP2000 and JP-LS support

With regards to creating compressed NIfTIs, dcm2niix can be compiled to use one of 3 internal compressors: miniz (slightly slower than zlib), system zlib, or cloudflare zlib (about twice as quick as zlib). The make command only compiles with the native compressor, the cmake provides arguments to make any compile. Regardless of the internal compressor, you will always get optimal compression speed by installed pigz and using it as your external compressor. Assuming a modern version of pigz, the fastest way to create gzipped files is the optimal (-z o) argument for dcm2niix, dcm2niix -z o /path/to/DICOMs. This method transfers uncompressed memory directly to pigz, rather than creating an uncompressed file and having pigz compress that file. The major strength and limitation of pigz is that it is multi-threaded, so it may not be suitable to instances where you want all processing constrained to a single thread. Another weakness of pigz is that assumes Unix style UTF8 file names, Windows UTF16 filenames can cause issues - e.g. if you have a folder name with non-Latin characters.

@neurolabusc
Copy link
Collaborator

The latest commit introduces the option to use the system zlib during compilation with make. This approach results in a slightly smaller executable because the compression library is not statically included. However, since it dynamically links to zlib, the zlib library must be installed on your system for the executable to function properly.

To compile with the system zlib, use the following command:

JPEGLS=1 ZLIB=1 make jp2

@dazzag24
Copy link
Author

Thanks, I was already using -z y which i think is equivalent to -z o. I've added the JPEGLS=1 line.

@neurolabusc
Copy link
Collaborator

@dazzag24 note that while both the arguments -z y and -z o can use pigz to accelerate compression, they are slightly different. The -z y will write each NIfTI file to disk uncompressed and then compress the file, analogous to running the command pigz --no-time -n -f -6 /path/to/image.nii'. On the other hand, using -z o on a Unix computer will pipe the raw data directly to pigz, without creating the intermediate uncompressed file. This piped operation requires you have pigz 2.3.4 (released Oct 2, 2016) or later. The difference is negligible for fast local SSD storage, but can be substantial if you are saving NIfTI images to networked storage.

So the options are:

argument notes
-z n create uncompressed NIfTIs
-z y use pigz if found (intermediate file), else use internal compressor
-z o use pigz if found (piped data), else use internal compressor
-z i use internal compressor

If you run dcm2niix without any arguments, the help file will tell you if it was compiled with miniz or zlib as an internal compressor. HOwever, it does not distringuish between the system zlib and cloudflare zlib as they are both zlib compatible libraries.

 -z : gz compress images (y/o/i/n/3, default n) [y=pigz, o=optimal pigz, i=internal:zlib, n=no, 3=no,3D]
 -z : gz compress images (y/o/i/n/3, default n) [y=pigz, o=optimal pigz, i=internal:miniz, n=no, 3=no,3D]

@dazzag24
Copy link
Author

dazzag24 commented Jan 2, 2025 via email

@neurolabusc
Copy link
Collaborator

I have updated the wiki

neurolabusc added a commit that referenced this issue Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants