diff --git a/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx b/Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx index 888412067..43df3ddb1 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/Testing/Source/MediaStorageAndFileFormat/Cxx/CMakeLists.txt b/Testing/Source/MediaStorageAndFileFormat/Cxx/CMakeLists.txt index 56c6fcf22..25671857a 100644 --- a/Testing/Source/MediaStorageAndFileFormat/Cxx/CMakeLists.txt +++ b/Testing/Source/MediaStorageAndFileFormat/Cxx/CMakeLists.txt @@ -39,6 +39,7 @@ set(MSFF_TEST_SRCS TestOrientation.cxx TestIconImage.cxx TestImageHelper.cxx + TestImageHelper3.cxx TestImageToImageFilter.cxx TestImageChangeTransferSyntax1.cxx #TestImageChangePhotometricInterpretation.cxx diff --git a/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper.cxx b/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper.cxx index 714141d16..51515cb1f 100644 --- a/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper.cxx +++ b/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper.cxx @@ -19,6 +19,8 @@ int TestImageHelper(int, char *[]) { + // Test written before DICOM CP 2330, + if( gdcm::ImageHelper::GetSecondaryCaptureImagePlaneModule() ) return 1; // gdcm::ImageHelper sh; const double pos[] = { 0,0,0, 1,1,1}; diff --git a/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper3.cxx b/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper3.cxx new file mode 100644 index 000000000..0619ffc54 --- /dev/null +++ b/Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper3.cxx @@ -0,0 +1,124 @@ +/*========================================================================= + + Program: GDCM (Grassroots DICOM). A DICOM library + + Copyright (c) 2006-2011 Mathieu Malaterre + All rights reserved. + See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "gdcmImageHelper.h" +#include "gdcmMediaStorage.h" +#include "gdcmDataSet.h" +#include "gdcmAttribute.h" +#include "gdcmDirectionCosines.h" +#include "gdcmImage.h" +#include "gdcmFile.h" + +int TestImageHelper3(int, char *[]) +{ + + std::vector impos; + impos.resize(3); + impos[0] = 1; + impos[1] = 2; + impos[2] = 3; + std::vector spacing; + spacing.resize(3); + spacing[0] = 0.6; + spacing[1] = 0.5; + spacing[2] = 0.4; + + std::vector dircos; + dircos.resize(6); + dircos[0] = 0; + dircos[1] = 1; + dircos[2] = 0; + dircos[3] = 1; + dircos[4] = 0; + dircos[5] = 0; + + gdcm::Image img; + img.SetNumberOfDimensions(3); + img.SetOrigin( impos.data() ); + img.SetSpacing( spacing.data() ); + img.SetDirectionCosines( dircos.data() ); + // Try SC + gdcm::File file; +{ + gdcm::ImageHelper::SetSecondaryCaptureImagePlaneModule( false ); + gdcm::MediaStorage ms( gdcm::MediaStorage::SecondaryCaptureImageStorage ); + gdcm::DataSet&ds = file.GetDataSet(); + + gdcm::DataElement de( gdcm::Tag(0x0008, 0x0016) ); + const char* msstr = gdcm::MediaStorage::GetMSString(ms); + de.SetByteValue( msstr, (uint32_t)strlen(msstr) ); + de.SetVR( gdcm::Attribute<0x0008, 0x0016>::GetVR() ); + ds.Insert( de ); + + gdcm::ImageHelper::SetDirectionCosinesValue( ds, dircos ); + gdcm::ImageHelper::SetOriginValue( ds, img ); + gdcm::ImageHelper::SetSpacingValue( ds, spacing ); + + std::cout << ds << std::endl; + + gdcm::Tag nominal(0x0018,0x2010 ); + if( !ds.FindDataElement( nominal ) ) return 1; + gdcm::Tag iop(0x0020,0x0037); + if( ds.FindDataElement( iop ) ) return 1; + + std::vector ret = gdcm::ImageHelper::GetSpacingValue( file ); + // warning;: only two dim: + if( ret[0] != spacing[0] || ret[1] != spacing[1] ) { + std::cerr << ret[0] << "," << ret[1] << std::endl; + return 1; + } +} + gdcm::File cp2330file; +{ + // New CP 2330 behavior + gdcm::ImageHelper::SetSecondaryCaptureImagePlaneModule( true ); + gdcm::MediaStorage ms( gdcm::MediaStorage::SecondaryCaptureImageStorage ); + gdcm::DataSet&ds = cp2330file.GetDataSet(); + + gdcm::DataElement de( gdcm::Tag(0x0008, 0x0016) ); + const char* msstr = gdcm::MediaStorage::GetMSString(ms); + de.SetByteValue( msstr, (uint32_t)strlen(msstr) ); + de.SetVR( gdcm::Attribute<0x0008, 0x0016>::GetVR() ); + ds.Insert( de ); + + // New CP 2330 behavior + gdcm::ImageHelper::SetDirectionCosinesValue( ds, dircos ); + gdcm::ImageHelper::SetOriginValue( ds, img ); + gdcm::ImageHelper::SetSpacingValue( ds, spacing ); + + std::cout << ds << std::endl; + + // previous call added the attribute + gdcm::Tag pixelspacing(0x0028,0x0030); + if( !ds.FindDataElement( pixelspacing ) ) return 1; + gdcm::Tag iop(0x0020,0x0037); + if( !ds.FindDataElement( iop ) ) return 1; + + std::vector ret = gdcm::ImageHelper::GetSpacingValue( cp2330file ); + if( ret != spacing ) { + std::cerr << ret[0] << "," << ret[1] << "," << ret[2] << std::endl; + return 1; + } + // make sure legacy still works: + ret = gdcm::ImageHelper::GetSpacingValue( file ); + if( ret[0] != spacing[0] || ret[1] != spacing[1] || ret[2] != 1 ) { + std::cerr << ret[0] << "," << ret[1] << std::endl; + return 1; + } + +} + + + std::cout << "success" << std::endl; + return 0; +}