From a6240d348df96840ff1f21a4f457fda94bbfc1e9 Mon Sep 17 00:00:00 2001 From: GDCM Upstream Date: Fri, 3 May 2024 05:12:32 -0700 Subject: [PATCH] GDCM 2024-05-03 (2eaae209) Code extracted from: https://github.com/malaterre/GDCM.git at commit 2eaae2091ed90566597c34886dd17d639c1ba8d5 (release). --- CMakeLists.txt | 3 +- .../gdcmPrivateDefaultDicts.cxx | 10 ++--- .../gdcmDataSet.h | 10 ++--- .../gdcmImageHelper.cxx | 37 +++++++++++++++++-- .../gdcmImageHelper.h | 1 + .../gdcmSplitMosaicFilter.cxx | 24 ++++++++---- 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 345f2973208..38c65d116c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ endif() #---------------------------------------------------------------------------- project(GDCM - VERSION 3.0.23 + VERSION 3.0.24 LANGUAGES CXX C ) ## NOTE: the "DESCRIPTION" feature of project() was introduced in cmake 3.10.0 @@ -564,6 +564,7 @@ if(APPLE) if(GDCM_USE_COREFOUNDATION_LIBRARY) find_library(COREFOUNDATION_LIBRARY CoreFoundation ) endif() + mark_as_advanced(COREFOUNDATION_LIBRARY) endif() #----------------------------------------------------------------------------- diff --git a/Source/DataDictionary/gdcmPrivateDefaultDicts.cxx b/Source/DataDictionary/gdcmPrivateDefaultDicts.cxx index 4c86aae8a0e..9016e436502 100644 --- a/Source/DataDictionary/gdcmPrivateDefaultDicts.cxx +++ b/Source/DataDictionary/gdcmPrivateDefaultDicts.cxx @@ -7418,11 +7418,11 @@ static const DICT_ENTRY DICOMV3DataDict [] = { /* end of dups */ {0x2005,0x0032,"Philips MR Imaging DD 002",VR::SQ,VM::VM1,"Protocol Data Sequence",false }, {0x2005,0x0034,"Philips MR Imaging DD 002",VR::LT,VM::VM1,"?SeriesTransactionUID?",false }, - {0x2005,0x0037,"Philips MR Imaging DD 002",VR::PN,VM::VM1,"Protocol Data Name",false }, - {0x2005,0x0038,"Philips MR Imaging DD 002",VR::PN,VM::VM1,"?MRApplicationName",false }, - {0x2005,0x0039,"Philips MR Imaging DD 002",VR::PN,VM::VM1,"Protocol Data Type",false }, - {0x2005,0x0040,"Philips MR Imaging DD 002",VR::PN,VM::VM1,"?MRVersionStr",false }, - {0x2005,0x0041,"Philips MR Imaging DD 002",VR::PN,VM::VM1,"?MRCommentStr",false }, + {0x2005,0x0037,"Philips MR Imaging DD 002",VR::LO,VM::VM1,"Protocol Data Name",false }, + {0x2005,0x0038,"Philips MR Imaging DD 002",VR::LO,VM::VM1,"?MRApplicationName",false }, + {0x2005,0x0039,"Philips MR Imaging DD 002",VR::LO,VM::VM1,"Protocol Data Type",false }, + {0x2005,0x0040,"Philips MR Imaging DD 002",VR::LO,VM::VM1,"?MRVersionStr",false }, + {0x2005,0x0041,"Philips MR Imaging DD 002",VR::LO,VM::VM1,"?MRCommentStr",false }, {0x2005,0x0043,"Philips MR Imaging DD 002",VR::SL,VM::VM1,"Protocol Data Block Length (non-padded)",false }, {0x2005,0x0044,"Philips MR Imaging DD 002",VR::OW,VM::VM1,"Protocol Data Block",false }, {0x2005,0x0047,"Philips MR Imaging DD 002",VR::CS,VM::VM1,"Protocol Data Boolean",false }, diff --git a/Source/DataStructureAndEncodingDefinition/gdcmDataSet.h b/Source/DataStructureAndEncodingDefinition/gdcmDataSet.h index 5f26a3dd449..3ed85b2a973 100644 --- a/Source/DataStructureAndEncodingDefinition/gdcmDataSet.h +++ b/Source/DataStructureAndEncodingDefinition/gdcmDataSet.h @@ -209,13 +209,9 @@ class GDCM_EXPORT DataSet // DUMB: this only search within the level of the current DataSet bool FindDataElement(const Tag &t) const { - const DataElement r(t); - //ConstIterator it = DES.find(r); - if( DES.find(r) != DES.end() ) - { - return true; - } - return false; + const auto it = GetDataElement(t); + // Return if tag is found + return it != GetDEEnd(); } // WARNING: diff --git a/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx b/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx index 888412067f7..43df3ddb1eb 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; +// By default, this is off, if you want behavior documented in DICOM CP 2330, turn it on bool ImageHelper::SecondaryCaptureImagePlaneModule = false; static bool GetOriginValueFromSequence(const DataSet& ds, const Tag& tfgs, std::vector &ori) @@ -1278,6 +1279,9 @@ Tag ImageHelper::GetSpacingTagFromMediaStorage(MediaStorage const &ms) if( ImageHelper::SecondaryCaptureImagePlaneModule ) { // Make SecondaryCaptureImagePlaneModule act as ForcePixelSpacing // This is different from Basic Pixel Spacing Calibration Macro Attributes + // + // Per the note: https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_A.8.html#sect_A.8.1.3 + gdcmDebugMacro( "FIXME: Multiple tags can identify Secondary Capture spacing. This function should not be used for Secondary Capture data." ); t = Tag(0x0028,0x0030); } else { t = Tag(0x0018,0x2010); @@ -1372,6 +1376,13 @@ Warning - Dicom dataset contains attributes not present in standard DICOM IOD - case MediaStorage::UltrasoundMultiFrameImageStorageRetired: // SC: case MediaStorage::SecondaryCaptureImageStorage: + // (0018,0088) DS [3] # 2, 1 SpacingBetweenSlices + if( ImageHelper::SecondaryCaptureImagePlaneModule ) { + t = Tag(0x0018,0x0088); + } else { + t = Tag(0xffff,0xffff); + } + break; case MediaStorage::MultiframeSingleBitSecondaryCaptureImageStorage: case MediaStorage::MultiframeGrayscaleByteSecondaryCaptureImageStorage: case MediaStorage::MultiframeGrayscaleWordSecondaryCaptureImageStorage: @@ -1471,7 +1482,25 @@ std::vector ImageHelper::GetSpacingValue(File const & f) } } - Tag spacingtag = GetSpacingTagFromMediaStorage(ms); + Tag spacingtag = Tag(0xffff,0xffff); + if( ms == MediaStorage::SecondaryCaptureImageStorage && SecondaryCaptureImagePlaneModule ) + { + // See the note: https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_A.8.html#sect_A.8.1.3 + if( ds.FindDataElement( Tag(0x0028,0x0030) ) ) + { + // Type 1C in 'SC Image' (for calibrated images) + spacingtag = Tag(0x0028,0x0030); + } + else if( ds.FindDataElement( Tag(0x0018,0x2010) ) ) + { + // Type 3 in 'SC Image' + spacingtag = Tag(0x0018,0x2010); + } + } + else + { + spacingtag = GetSpacingTagFromMediaStorage(ms); + } if( spacingtag != Tag(0xffff,0xffff) && ds.FindDataElement( spacingtag ) && !ds.GetDataElement( spacingtag ).IsEmpty() ) { const DataElement& de = ds.GetDataElement( spacingtag ); @@ -1972,7 +2001,7 @@ void ImageHelper::SetOriginValue(DataSet & ds, const Image & image) ms.SetFromDataSet(ds); assert( MediaStorage::IsImage( ms ) ); - if( ms == MediaStorage::SecondaryCaptureImageStorage ) + if( ms == MediaStorage::SecondaryCaptureImageStorage && !ImageHelper::SecondaryCaptureImagePlaneModule ) { // https://sourceforge.net/p/gdcm/bugs/322/ // default behavior is simply to pass @@ -1986,6 +2015,7 @@ void ImageHelper::SetOriginValue(DataSet & ds, const Image & image) && ms != MediaStorage::PETImageStorage //&& ms != MediaStorage::ComputedRadiographyImageStorage && ms != MediaStorage::SegmentationStorage + && ms != MediaStorage::SecondaryCaptureImageStorage /* CP 2330 */ && ms != MediaStorage::MultiframeSingleBitSecondaryCaptureImageStorage && ms != MediaStorage::MultiframeGrayscaleByteSecondaryCaptureImageStorage && ms != MediaStorage::MultiframeGrayscaleWordSecondaryCaptureImageStorage @@ -2123,7 +2153,7 @@ void ImageHelper::SetDirectionCosinesValue(DataSet & ds, const std::vector GetSpacingValue(File const & f); + /// \warning You need to call SetSpacingValue after SetOriginValue / SetDirectionCosinesValue static void SetSpacingValue(DataSet & ds, const std::vector & spacing); /// DO NOT USE diff --git a/Source/MediaStorageAndFileFormat/gdcmSplitMosaicFilter.cxx b/Source/MediaStorageAndFileFormat/gdcmSplitMosaicFilter.cxx index 6f368e0c51a..047432b3309 100644 --- a/Source/MediaStorageAndFileFormat/gdcmSplitMosaicFilter.cxx +++ b/Source/MediaStorageAndFileFormat/gdcmSplitMosaicFilter.cxx @@ -271,7 +271,7 @@ bool SplitMosaicFilter::ComputeMOSAICSliceNormal( double slicenormalvector[3], b } else if( fabs(-1. - snv_dot) < 1e-6 ) { - gdcmWarningMacro("SliceNormalVector is opposite direction"); + gdcmDebugMacro("SliceNormalVector is opposite direction"); inverted = true; } else @@ -298,6 +298,10 @@ bool SplitMosaicFilter::ComputeMOSAICImagePositionPatient( double ret[3], DataSet& ds = GetFile().GetDataSet(); DirectionCosines dc( dircos ); dc.Normalize(); + double z[3]={}; + dc.Cross (z); + DirectionCosines::Normalize(z); + const double *dircos_normalized = dc; const double *x = dircos_normalized; const double *y = dircos_normalized + 3; @@ -315,9 +319,8 @@ bool SplitMosaicFilter::ComputeMOSAICImagePositionPatient( double ret[3], if( size ) { // two cases: if( size == mosaic_dims[2] ) { - // all mosaic have there own slice position, simply need to pick the right one - // Handle inverted case: - size_t index = inverted ? size - 1 : 0; + // all mosaic have there own slice position, always pick the first one for computation: + size_t index = 0; MrProtocol::Slice & slice = sa.Slices[index]; MrProtocol::Vector3 & p = slice.Position; double pos[3]; @@ -327,12 +330,13 @@ bool SplitMosaicFilter::ComputeMOSAICImagePositionPatient( double ret[3], for(int i = 0; i < 3; ++i ) { ipp_csa[i] = pos[i] - mosaic_dims[0] / 2. * pixelspacing[0] * x[i] - mosaic_dims[1] / 2. * pixelspacing[1] * y[i]; } + int mult = mosaic_dims[2] % 2; + if( inverted ) { + ipp_csa[2] = ipp_csa[2] + mult * pixelspacing[2]; + } hasIppCsa = true; } else if( size == 1 /*&& mosaic_dims[2] % 2 == 0*/) { // there is a single SliceArray but multiple mosaics, assume this is exactly the center one - double z[3]={}; - dc.Cross(z); - DirectionCosines::Normalize(z); size_t index = 0; MrProtocol::Slice & slice = sa.Slices[index]; MrProtocol::Vector3 & p = slice.Position; @@ -359,6 +363,12 @@ bool SplitMosaicFilter::ComputeMOSAICImagePositionPatient( double ret[3], ipp_dcm[i] = ipp[i] + (image_dims[0] - mosaic_dims[0]) / 2. * pixelspacing[0] * x[i] + (image_dims[1] - mosaic_dims[1]) / 2. * pixelspacing[1] * y[i] ; } + // When SNV inverted, first slice is actually the last one: + if( inverted ) { + for(int i = 0; i < 3; ++i ) { + ipp_dcm[i] = ipp_dcm[i] - z[i] * pixelspacing[2] * (mosaic_dims[2] - 1); + } + } } if(hasIppCsa ) { double diff[3];