From 06091299f2227f8470a4468821d231b760337ca0 Mon Sep 17 00:00:00 2001 From: Mathieu Malaterre Date: Mon, 25 Mar 2024 00:46:33 -0700 Subject: [PATCH] Image Plane Module for SecondaryCaptureImageStorage Create an extension for SecondaryCaptureImageStorage as if Image Plane Module would be optional. This will allow reading IPP/IOP/PixelSpacing for SecondaryCaptureImageStorage for some particular dataset. Make this feature opt-in per the discussion: * https://github.com/malaterre/GDCM/pull/158 This extension does not implement Basic Pixel Spacing Calibration Macro Attributes since the original target dataset to support is as follow: $ dcmdump 000198cd-cd88-4760-97d1-21bbea047fff.dcm | grep Spacin (0018,0088) DS [1] # 2, 1 SpacingBetweenSlices (0028,0030) DS [.33\.33] # 8, 2 PixelSpacing > If Pixel Spacing Calibration Type (0028,0A02) and Imager Pixel Spacing > (0018,1164) and Nominal Scanned Pixel Spacing (0018,2010) are absent, > then it cannot be determined whether or not correction or calibration > have been performed. References: * https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.8.6.2.html#table_C.8-25 * https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_10.7.html#table_10-10 Co-authored-by: Matt McCormick Co-authored-by: Mihail Isakov Co-authored-by: Steve Pieper --- Applications/Cxx/gdcminfo.cxx | 4 ++++ .../gdcmImageHelper.cxx | 23 +++++++++++++++++-- .../gdcmImageHelper.h | 8 +++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Applications/Cxx/gdcminfo.cxx b/Applications/Cxx/gdcminfo.cxx index ff9508535b..fc82e869ad 100644 --- a/Applications/Cxx/gdcminfo.cxx +++ b/Applications/Cxx/gdcminfo.cxx @@ -350,6 +350,7 @@ static void PrintHelp() std::cout << " --force-rescale force rescale." << std::endl; std::cout << " --force-spacing force spacing." << std::endl; std::cout << " --mosaic dump image information of MOSAIC." << std::endl; + std::cout << " --scipm Include Image Plane Module for Secondary Capture Image." << std::endl; std::cout << "General Options:" << std::endl; std::cout << " -V --verbose more verbose (warning+error)." << std::endl; @@ -626,6 +627,7 @@ int main(int argc, char *argv[]) std::string xmlpath; int forcerescale = 0; int forcespacing = 0; + int scipm = 0; int resourcespath = 0; int verbose = 0; @@ -647,6 +649,7 @@ int main(int argc, char *argv[]) {"force-rescale", 0, &forcerescale, 1}, {"force-spacing", 0, &forcespacing, 1}, {"mosaic", 0, &mosaic, 1}, + {"scipm", 0, &scipm, 1}, {"verbose", 0, &verbose, 1}, {"warning", 0, &warning, 1}, @@ -769,6 +772,7 @@ int main(int argc, char *argv[]) gdcm::ImageHelper::SetForceRescaleInterceptSlope(forcerescale ? true : false); gdcm::ImageHelper::SetForcePixelSpacing(forcespacing ? true : false); + gdcm::ImageHelper::SetSecondaryCaptureImagePlaneModule(scipm ? true : false); if( filename.empty() ) { diff --git a/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx b/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx index f69110a291..888412067f 100644 --- a/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx +++ b/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx @@ -48,6 +48,7 @@ namespace gdcm bool ImageHelper::ForceRescaleInterceptSlope = false; bool ImageHelper::PMSRescaleInterceptSlope = true; bool ImageHelper::ForcePixelSpacing = false; +bool ImageHelper::SecondaryCaptureImagePlaneModule = false; static bool GetOriginValueFromSequence(const DataSet& ds, const Tag& tfgs, std::vector &ori) { @@ -578,7 +579,7 @@ std::vector ImageHelper::GetOriginValue(File const & f) // else const Tag timagepositionpatient(0x0020, 0x0032); - if( ms != MediaStorage::SecondaryCaptureImageStorage && ds.FindDataElement( timagepositionpatient ) ) + if( (ms != MediaStorage::SecondaryCaptureImageStorage || SecondaryCaptureImagePlaneModule) && ds.FindDataElement( timagepositionpatient ) ) { const DataElement& de = ds.GetDataElement( timagepositionpatient ); Attribute<0x0020,0x0032> at = {{0,0,0}}; // default value if empty @@ -730,7 +731,7 @@ std::vector ImageHelper::GetDirectionCosinesValue(File const & f) } dircos.resize( 6 ); - if( ms == MediaStorage::SecondaryCaptureImageStorage || !GetDirectionCosinesFromDataSet(ds, dircos) ) + if( (ms == MediaStorage::SecondaryCaptureImageStorage && !SecondaryCaptureImagePlaneModule) || !GetDirectionCosinesFromDataSet(ds, dircos) ) { dircos[0] = 1; dircos[1] = 0; @@ -774,6 +775,16 @@ bool ImageHelper::GetForcePixelSpacing() return ForcePixelSpacing; } +void ImageHelper::SetSecondaryCaptureImagePlaneModule(bool b) +{ + SecondaryCaptureImagePlaneModule = b; +} + +bool ImageHelper::GetSecondaryCaptureImagePlaneModule() +{ + return SecondaryCaptureImagePlaneModule; +} + bool GetRescaleInterceptSlopeValueFromDataSet(const DataSet& ds, std::vector & interceptslope) { Attribute<0x0028,0x1052> at1; @@ -1264,6 +1275,14 @@ Tag ImageHelper::GetSpacingTagFromMediaStorage(MediaStorage const &ms) t = Tag(0x3002,0x0011); // ImagePlanePixelSpacing break; case MediaStorage::SecondaryCaptureImageStorage: + if( ImageHelper::SecondaryCaptureImagePlaneModule ) { + // Make SecondaryCaptureImagePlaneModule act as ForcePixelSpacing + // This is different from Basic Pixel Spacing Calibration Macro Attributes + t = Tag(0x0028,0x0030); + } else { + t = Tag(0x0018,0x2010); + } + break; case MediaStorage::MultiframeSingleBitSecondaryCaptureImageStorage: case MediaStorage::MultiframeGrayscaleByteSecondaryCaptureImageStorage: case MediaStorage::MultiframeGrayscaleWordSecondaryCaptureImageStorage: diff --git a/Source/MediaStorageAndFileFormat/gdcmImageHelper.h b/Source/MediaStorageAndFileFormat/gdcmImageHelper.h index 2f70231d4b..764939f4db 100644 --- a/Source/MediaStorageAndFileFormat/gdcmImageHelper.h +++ b/Source/MediaStorageAndFileFormat/gdcmImageHelper.h @@ -85,6 +85,13 @@ class GDCM_EXPORT ImageHelper static void SetForcePixelSpacing(bool); static bool GetForcePixelSpacing(); + /// Opt into Image Plane Module for Secondary Capture Image Storage + /// Enable reading Image Position Patient (IPP), Image Orientation Patient + /// (IOP) and Pixel Spacing (0028,0030) + /// This is a custom extension for some existing dataset (academic) + static void SetSecondaryCaptureImagePlaneModule(bool); + static bool GetSecondaryCaptureImagePlaneModule(); + /// This function checks tags (0x0028, 0x0010) and (0x0028, 0x0011) for the /// rows and columns of the image in pixels (as opposed to actual distances). /// The output is {col , row} @@ -156,6 +163,7 @@ class GDCM_EXPORT ImageHelper static bool ForceRescaleInterceptSlope; static bool PMSRescaleInterceptSlope; static bool ForcePixelSpacing; + static bool SecondaryCaptureImagePlaneModule; }; } // end namespace gdcm