From 742543a88bd39c5e44552b2a0ea9043aeb59ec61 Mon Sep 17 00:00:00 2001 From: Andrey Fedorov Date: Tue, 25 Jun 2024 13:57:24 -0400 Subject: [PATCH] ENH: support source image references in the shared FG Re #489 --- apps/seg/itkimage2segimage.cxx | 3 +- apps/seg/itkimage2segimage.xml | 9 +++ include/dcmqi/Itk2DicomConverter.h | 6 +- libsrc/Itk2DicomConverter.cpp | 126 +++++++++++++++++++---------- 4 files changed, 99 insertions(+), 45 deletions(-) diff --git a/apps/seg/itkimage2segimage.cxx b/apps/seg/itkimage2segimage.cxx index 434039ea..4377bb1d 100644 --- a/apps/seg/itkimage2segimage.cxx +++ b/apps/seg/itkimage2segimage.cxx @@ -126,7 +126,8 @@ int main(int argc, char *argv[]) segmentations, metadata, skipEmptySlices, - useLabelIDAsSegmentNumber); + useLabelIDAsSegmentNumber, + referencesGeometryCheck); if (result == NULL){ std::cerr << "ERROR: Conversion failed." << std::endl; diff --git a/apps/seg/itkimage2segimage.xml b/apps/seg/itkimage2segimage.xml index 8fafec31..606d67fc 100644 --- a/apps/seg/itkimage2segimage.xml +++ b/apps/seg/itkimage2segimage.xml @@ -64,6 +64,15 @@ Skip empty slices while encoding segmentation image. By default, empty slices will not be encoded, resulting in a smaller output file size. + + referencesGeometryCheck + + input + referencesGeometryCheck + 1 + Check whether referenced image slices match the geometry of the segmentation before adding them to the references list. Set this flag to 0 to bypass these checks and add references independently of the geometry consistency. + + useLabelIDAsSegmentNumber diff --git a/include/dcmqi/Itk2DicomConverter.h b/include/dcmqi/Itk2DicomConverter.h index 2c0c257b..d89b9d2d 100644 --- a/include/dcmqi/Itk2DicomConverter.h +++ b/include/dcmqi/Itk2DicomConverter.h @@ -57,13 +57,17 @@ namespace dcmqi { * are updated by this flag, compared to the default behavior (false). Of course, the * resulting DICOM object is still valid, dimensions are consistent and lead to meaningful * display. + * @param referencesGeometryCheck A boolean indicating whether the conversion process should attempt checking if the geometry of the referenced DICOM images is consistent with the corresponding slices of the segmentation. + * By default, this check is enabled. If disabled, all of the references will be + * added in the SharedFunctionalGroupsSequence without any geometry checks. * @return A pointer to the resulting DICOM Segmentation object. */ static DcmDataset* itkimage2dcmSegmentation(vector dcmDatasets, vector segmentations, const string &metaData, bool skipEmptySlices=true, - bool useLabelIDAsSegmentNumber=false); + bool useLabelIDAsSegmentNumber=false, + bool referencesGeometryCheck=true); protected: diff --git a/libsrc/Itk2DicomConverter.cpp b/libsrc/Itk2DicomConverter.cpp index 855c91fa..694cc9e4 100644 --- a/libsrc/Itk2DicomConverter.cpp +++ b/libsrc/Itk2DicomConverter.cpp @@ -29,7 +29,8 @@ namespace dcmqi { vector segmentations, const string &metaData, bool skipEmptySlices, - bool useLabelIDAsSegmentNumber) { + bool useLabelIDAsSegmentNumber, + bool referencesGeometryCheck) { ShortImageType::SizeType inputSize = segmentations[0]->GetBufferedRegion().GetSize(); @@ -108,7 +109,6 @@ namespace dcmqi { delete pixmsr; } - // Iterate over the files and labels available in each file, create a segment for each label, // initialize segment frames and add to the document @@ -125,6 +125,38 @@ namespace dcmqi { CHECK_COND(dcmDatasets[0]->findAndGetOFString(DCM_SeriesInstanceUID, seriesInstanceUID)); CHECK_COND(refseriesItem->setSeriesInstanceUID(seriesInstanceUID)); + // Shared FGs: DerivationImageSequence + if(!referencesGeometryCheck && dcmDatasets.size() > 1){ + FGDerivationImage *fgder = new FGDerivationImage(); + DerivationImageItem *derimgItem; + + DSRBasicCodedEntry code_seg=CODE_DCM_Segmentation_113076; + CHECK_COND(fgder->addDerivationImageItem(CodeSequenceMacro(code_seg.CodeValue,code_seg.CodingSchemeDesignator, + code_seg.CodeMeaning),"",derimgItem)); + + OFVector srcimgItems; + derimgItem->addSourceImageItems(dcmDatasets, + CodeSequenceMacro(code_seg.CodeValue,code_seg.CodingSchemeDesignator, code_seg.CodeMeaning), srcimgItems, OFTrue /*skip file errors */); + + for(size_t src_image_cnt=0;src_image_cntgetImageSOPInstanceReference(); + OFString instanceUID; + CHECK_COND(instRef.getReferencedSOPClassUID(classUID)); + CHECK_COND(instRef.getReferencedSOPInstanceUID(instanceUID)); + + if(instanceUIDs.find(instanceUID) == instanceUIDs.end()){ + SOPInstanceReferenceMacro *refinstancesItem = new SOPInstanceReferenceMacro(); + CHECK_COND(refinstancesItem->setReferencedSOPClassUID(classUID)); + CHECK_COND(refinstancesItem->setReferencedSOPInstanceUID(instanceUID)); + refinstances.push_back(refinstancesItem); + instanceUIDs.insert(instanceUID); + } + } + CHECK_COND(segdoc->addForAllFrames(*fgder)); + delete fgder; + } + int uidfound = 0, uidnotfound = 0; Uint8 *frameData = new Uint8[frameSize]; @@ -137,10 +169,15 @@ namespace dcmqi { for(size_t segFileNumber=0; segFileNumber > slice2derimg = getSliceMapForSegmentation2DerivationImage(dcmDatasets, segmentations[segFileNumber]); - for(vector >::const_iterator vI=slice2derimg.begin();vI!=slice2derimg.end();++vI) - if((*vI).size()>0) - hasDerivationImages = true; + vector > slice2derimg; + if(referencesGeometryCheck){ + slice2derimg = getSliceMapForSegmentation2DerivationImage(dcmDatasets, segmentations[segFileNumber]); + for(vector >::const_iterator vI=slice2derimg.begin();vI!=slice2derimg.end();++vI) + if((*vI).size()>0) + hasDerivationImages = true; + } else { + hasDerivationImages = false; + } perFrameFGs.clear(); perFrameFGs.push_back(fgppp); @@ -360,42 +397,44 @@ namespace dcmqi { } OFVector siVector; - for(size_t derImageInstanceNum=0; - derImageInstanceNum0){ - - DerivationImageItem *derimgItem; - DSRBasicCodedEntry code_seg=CODE_DCM_Segmentation_113076; - CHECK_COND(fgder->addDerivationImageItem(CodeSequenceMacro(code_seg.CodeValue,code_seg.CodingSchemeDesignator, - code_seg.CodeMeaning),"",derimgItem)); - - DSRBasicCodedEntry code = CODE_DCM_SourceImageForImageProcessingOperation; - OFVector srcimgItems; - CHECK_COND(derimgItem->addSourceImageItems(siVector, - CodeSequenceMacro(code.CodeValue, code.CodingSchemeDesignator, - code.CodeMeaning), - srcimgItems)); - - { - // initialize class UID and series instance UID - ImageSOPInstanceReferenceMacro &instRef = srcimgItems[0]->getImageSOPInstanceReference(); - OFString instanceUID; - CHECK_COND(instRef.getReferencedSOPClassUID(classUID)); - CHECK_COND(instRef.getReferencedSOPInstanceUID(instanceUID)); - - if(instanceUIDs.find(instanceUID) == instanceUIDs.end()){ - SOPInstanceReferenceMacro *refinstancesItem = new SOPInstanceReferenceMacro(); - CHECK_COND(refinstancesItem->setReferencedSOPClassUID(classUID)); - CHECK_COND(refinstancesItem->setReferencedSOPInstanceUID(instanceUID)); - refinstances.push_back(refinstancesItem); - instanceUIDs.insert(instanceUID); - uidnotfound++; - } else { - uidfound++; + if(siVector.size()>0){ + + DerivationImageItem *derimgItem; + DSRBasicCodedEntry code_seg=CODE_DCM_Segmentation_113076; + CHECK_COND(fgder->addDerivationImageItem(CodeSequenceMacro(code_seg.CodeValue,code_seg.CodingSchemeDesignator, + code_seg.CodeMeaning),"",derimgItem)); + + DSRBasicCodedEntry code = CODE_DCM_SourceImageForImageProcessingOperation; + OFVector srcimgItems; + CHECK_COND(derimgItem->addSourceImageItems(siVector, + CodeSequenceMacro(code.CodeValue, code.CodingSchemeDesignator, + code.CodeMeaning), + srcimgItems)); + + { + // initialize class UID and series instance UID + ImageSOPInstanceReferenceMacro &instRef = srcimgItems[0]->getImageSOPInstanceReference(); + OFString instanceUID; + CHECK_COND(instRef.getReferencedSOPClassUID(classUID)); + CHECK_COND(instRef.getReferencedSOPInstanceUID(instanceUID)); + + if(instanceUIDs.find(instanceUID) == instanceUIDs.end()){ + SOPInstanceReferenceMacro *refinstancesItem = new SOPInstanceReferenceMacro(); + CHECK_COND(refinstancesItem->setReferencedSOPClassUID(classUID)); + CHECK_COND(refinstancesItem->setReferencedSOPInstanceUID(instanceUID)); + refinstances.push_back(refinstancesItem); + instanceUIDs.insert(instanceUID); + uidnotfound++; + } else { + uidfound++; + } } } } @@ -403,7 +442,7 @@ namespace dcmqi { CHECK_COND(segdoc->addFrame(frameData, segmentNumber, perFrameFGs)); // remove derivation image FG from the per-frame FGs, only if applicable! - if(siVector.size()>0){ + if(referencesGeometryCheck && siVector.size()>0){ // clean up for the next frame fgder->clearData(); } @@ -418,7 +457,8 @@ namespace dcmqi { delete fgfc; delete fgppp; - delete fgder; + if(referencesGeometryCheck) + delete fgder; segdoc->getSeries().setSeriesNumber(metaInfo.getSeriesNumber().c_str());