ENH: Update GDCM to v3.2.8#6520
Conversation
Code extracted from:
https://github.com/malaterre/GDCM.git
at commit 2b9238088772254f66b2764b7e4a18e011c63926 (v3.2.8).
* upstream-GDCM: GDCM 2026-06-18 (2b923808) # Conflicts: # Modules/ThirdParty/GDCM/src/gdcm/CMakeLists.txt # Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/CMakeLists.txt # Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmJPEGCodec.cxx # Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmJPEGTurboCodec.cxx # Modules/ThirdParty/GDCM/src/gdcm/Utilities/CMakeLists.txt
GDCM's CMakeLists.txt guards find_package() calls with checks on ZLIB_LIBRARIES and EXPAT_LIBRARIES (plural forms). Set these from ITK's bundled library targets so the guards work correctly.
|
| Filename | Overview |
|---|---|
| Modules/ThirdParty/GDCM/src/gdcm/CMakeLists.txt | Updates the vendored GDCM version and dependency options for ITK-provided libraries. |
| Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmJPEG2000Codec.cxx | Adds packed 12-bit conversion before OpenJPEG image creation, with fragile fragment-size handling. |
| Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmJPEGLSCodec.cxx | Adds packed 12-bit unpacking before JPEG-LS encoding, but can encode the wrong sample precision. |
| Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmRLECodec.cxx | Adds packed 12-bit unpacking for RLE encoding, with one cleanup gap on exceptions. |
| Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmSplitGridFilter.cxx | Adds GRID image splitting, with issues in decode failure handling, slice order, and output metadata. |
| Modules/ThirdParty/GDCM/src/gdcm/Source/MediaStorageAndFileFormat/gdcmSplitMosaicFilter.cxx | Adds an ImageType sentinel before MOSAIC splitting. |
Reviews (1): Last reviewed commit: "ENH: Set plural library variables for GD..." | Re-trigger Greptile
| opj_image_destroy(image); | ||
| return nullptr; | ||
| } | ||
| actual_inputbuffer = unpacked_buffer; |
There was a problem hiding this comment.
Fragment Size Drives Unpack Writes
This branch sizes unpacked_buffer from the expected pixel count but lets Unpacker12Bits::Unpack write based on the actual fragment_size. In release builds the size check above is only an assert, so a padded or oversized 12-bit fragment can write past the allocation, while an odd packed byte count can reject otherwise valid dimensions.
| actual_indata = unpacked_buffer; | ||
| actual_inlen = unpacked_size; | ||
| // Update bitsallocated for encoder - 12-bit uses 16-bit container | ||
| bitsallocated = 16; |
There was a problem hiding this comment.
Sample Precision Becomes Container Width
After unpacking 12-bit pixels into a 16-bit buffer, this changes the value later passed to the JPEG-LS encoder's sample-depth path from 12 to 16. A BitsAllocated/BitsStored=12 image can then be encoded as a 16-bit JPEG-LS stream while the DICOM pixel metadata still describes 12 stored bits, so other readers can decode the compressed data with the wrong precision.
| const unsigned int * dimensions = this->GetDimensions(); | ||
| size_t npixels = (size_t)dimensions[0] * dimensions[1] * pf.GetSamplesPerPixel(); | ||
| size_t unpacked_size = npixels * 2; // 16-bit per pixel | ||
| unpacked_buffer = new char[unpacked_size]; |
There was a problem hiding this comment.
| unsigned long l = inputimage.GetBufferLength(); | ||
| std::vector<char> buf; | ||
| buf.resize(l); | ||
| inputimage.GetBuffer(buf.data()); |
There was a problem hiding this comment.
GetBuffer returns false when the input image cannot be decoded, but this path ignores that result and continues with the resized vector. A failed decode is therefore reshuffled into zero-filled output pixel data, and callers receive a successful-looking split image with incorrect pixels.
| inputimage.GetBuffer(buf.data()); | |
| if (!inputimage.GetBuffer(buf.data())) { | |
| return false; | |
| } |
| outbuf.resize(l); | ||
|
|
||
| bool b; | ||
| if (inverted && false) { |
There was a problem hiding this comment.
ComputeGRIDSliceNormal can set inverted, but this condition forces the inverted path to stay disabled. GRID images whose slice normal indicates reversed order are still reorganized with reorganize_grid, so the output volume can have the slice axis reversed relative to patient geometry.
| MediaStorage ms = MediaStorage::SecondaryCaptureImageStorage; | ||
| ms.SetFromFile(GetFile()); | ||
|
|
||
| if (ms == MediaStorage::MRImageStorage) { | ||
| // Ok make it a MediaStorage::EnhancedMRImageStorage | ||
| // ms = MediaStorage::EnhancedMRImageStorage; | ||
| // | ||
| // // Remove old MRImageStorage attribute then: | ||
| // ds.Remove( Tag(0x0020,0x0032) ); // Image Position (Patient) | ||
| // ds.Remove( Tag(0x0020,0x0037) ); // Image Orientation (Patient) | ||
| // ds.Remove( Tag(0x0028,0x1052) ); // Rescale Intercept | ||
| // ds.Remove( Tag(0x0028,0x1053) ); // Rescale Slope | ||
| // ds.Remove( Tag(0x0028,0x1054) ); // Rescale Type | ||
| } else { | ||
| gdcmDebugMacro("Expecting MRImageStorage"); | ||
| return false; | ||
| } | ||
| DataElement de(Tag(0x0008, 0x0016)); | ||
| const char* msstr = MediaStorage::GetMSString(ms); | ||
| VL::Type strlenMsstr = (VL::Type)strlen(msstr); | ||
| de.SetByteValue(msstr, strlenMsstr); | ||
| de.SetVR(Attribute<0x0008, 0x0016>::GetVR()); | ||
| ds.Replace(de); |
There was a problem hiding this comment.
Volume Keeps Single-Frame Storage
After the split sets the image to three dimensions with multiple slices, this block writes back the same MR Image Storage UID instead of converting the object to an enhanced or multiframe storage shape. The resulting DICOM advertises single-frame MR storage while carrying volume pixel data, which can make validators or downstream readers reject or misinterpret the output.
| if( nvalues < 2 || imtype[nvalues-1].Trim() != "MOSAIC" ) { | ||
| gdcmErrorMacro("Unhandled MOSAIC"); | ||
| return false; | ||
| } |
There was a problem hiding this comment.
ImageType Gate Rejects Mosaics
This new gate requires the last ImageType value to be exactly MOSAIC before the existing mosaic metadata checks run. Siemens mosaics whose ImageType was reordered or rewritten by a vendor/anonymizer can still contain the CSA and geometry data needed by the old splitter, but now return false immediately.
|
@greptile-apps These changes merged from upstream GDCM are outside your scope of review. |
|
Understood — upstream changes from GDCM are brought in as-is and aren't part of the review scope for this PR. I'll focus only on the ITK-specific modifications going forward. |
Summary
Update the vendored GDCM third-party library from commit 244fdbea to tag v3.2.8.
Changes
UpdateFromUpstream.shtag tov3.2.8itk_jpeg.h)find_package(ZLIB)andfind_package(EXPAT)when libraries are pre-set by ITKZLIB_LIBRARIES,EXPAT_LIBRARIES) in the parent CMakeLists for compatibilityUpstream Release Notes
https://github.com/malaterre/GDCM/releases/tag/v3.2.8
Testing
Generated by the
update-third-partyskill.