Skip to content

Commit

Permalink
Check for Secondary Capture spacing following DICOM Part 3 Sect A.8.1.3
Browse files Browse the repository at this point in the history
Following the Secondary Capture Image IOD Module Note:

> If Image Position (Patient) (0020,0032) and Image Orientation (Patient) (0020,0037) (from the Image Plane Module) are present, then the values of Pixel Spacing (0028,0030) (from the Image Plane Module and the Basic Pixel Spacing Calibration Macro included from the SC Image Module) are intended to be used for 3D spatial computations, rather than any values of Nominal Scanned Pixel Spacing (0018,2010) (from the SC Image Module), which may also be present.

Ref:
* https://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_A.8.html#sect_A.8.1.3

Co-authored-by: Mihail Isakov <[email protected]>
Co-authored-by: Matt McCormick <[email protected]>
Co-authored-by: Steve Pieper <[email protected]>
Co-authored-by: Sean McBride <[email protected]>
5 people committed Apr 29, 2024
1 parent 137039d commit 836f7a7
Showing 5 changed files with 162 additions and 3 deletions.
37 changes: 34 additions & 3 deletions Source/MediaStorageAndFileFormat/gdcmImageHelper.cxx
Original file line number Diff line number Diff line change
@@ -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<double> &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<double> 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<doubl
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
@@ -2136,6 +2166,7 @@ void ImageHelper::SetDirectionCosinesValue(DataSet & ds, const std::vector<doubl
&& ms != MediaStorage::RTDoseStorage
&& ms != MediaStorage::PETImageStorage
//&& ms != MediaStorage::ComputedRadiographyImageStorage
&& ms != MediaStorage::SecondaryCaptureImageStorage /* CP 2330 */
&& ms != MediaStorage::MultiframeSingleBitSecondaryCaptureImageStorage
&& ms != MediaStorage::MultiframeGrayscaleByteSecondaryCaptureImageStorage
&& ms != MediaStorage::MultiframeGrayscaleWordSecondaryCaptureImageStorage
1 change: 1 addition & 0 deletions Source/MediaStorageAndFileFormat/gdcmImageHelper.h
Original file line number Diff line number Diff line change
@@ -129,6 +129,7 @@ class GDCM_EXPORT ImageHelper

/// Set/Get Spacing from/to a File
static std::vector<double> GetSpacingValue(File const & f);
/// \warning You need to call SetSpacingValue after SetOriginValue / SetDirectionCosinesValue
static void SetSpacingValue(DataSet & ds, const std::vector<double> & spacing);

/// DO NOT USE
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ set(MSFF_TEST_SRCS
TestOrientation.cxx
TestIconImage.cxx
TestImageHelper.cxx
TestImageHelper3.cxx
TestImageToImageFilter.cxx
TestImageChangeTransferSyntax1.cxx
#TestImageChangePhotometricInterpretation.cxx
Original file line number Diff line number Diff line change
@@ -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};
124 changes: 124 additions & 0 deletions Testing/Source/MediaStorageAndFileFormat/Cxx/TestImageHelper3.cxx
Original file line number Diff line number Diff line change
@@ -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<double> impos;
impos.resize(3);
impos[0] = 1;
impos[1] = 2;
impos[2] = 3;
std::vector<double> spacing;
spacing.resize(3);
spacing[0] = 0.6;
spacing[1] = 0.5;
spacing[2] = 0.4;

std::vector<double> 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<double> 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<double> 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;
}

0 comments on commit 836f7a7

Please sign in to comment.