Skip to content

Commit

Permalink
Merge pull request #501 from QIICR/489-add-skip-geometry-check
Browse files Browse the repository at this point in the history
support source image references in the shared FG
  • Loading branch information
fedorov authored Jun 26, 2024
2 parents 2a2d359 + 742543a commit 504cc7b
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 45 deletions.
3 changes: 2 additions & 1 deletion apps/seg/itkimage2segimage.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 9 additions & 0 deletions apps/seg/itkimage2segimage.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
<description>Skip empty slices while encoding segmentation image. By default, empty slices will not be encoded, resulting in a smaller output file size.</description>
</integer>

<integer>
<name>referencesGeometryCheck</name>
<label>Check referenced source image geometry</label>
<channel>input</channel>
<longflag>referencesGeometryCheck</longflag>
<default>1</default>
<description>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.</description>
</integer>

<boolean>
<name>useLabelIDAsSegmentNumber</name>
<label>Use label ID as Segment Number</label>
Expand Down
6 changes: 5 additions & 1 deletion include/dcmqi/Itk2DicomConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<DcmDataset*> dcmDatasets,
vector<ShortImageType::Pointer> segmentations,
const string &metaData,
bool skipEmptySlices=true,
bool useLabelIDAsSegmentNumber=false);
bool useLabelIDAsSegmentNumber=false,
bool referencesGeometryCheck=true);

protected:

Expand Down
126 changes: 83 additions & 43 deletions libsrc/Itk2DicomConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ namespace dcmqi {
vector<ShortImageType::Pointer> segmentations,
const string &metaData,
bool skipEmptySlices,
bool useLabelIDAsSegmentNumber) {
bool useLabelIDAsSegmentNumber,
bool referencesGeometryCheck) {

ShortImageType::SizeType inputSize = segmentations[0]->GetBufferedRegion().GetSize();

Expand Down Expand Up @@ -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

Expand All @@ -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<SourceImageItem*> 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_cnt<srcimgItems.size();src_image_cnt++){
// initialize class UID and series instance UID
ImageSOPInstanceReferenceMacro &instRef = srcimgItems[src_image_cnt]->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);
}
}
CHECK_COND(segdoc->addForAllFrames(*fgder));
delete fgder;
}

int uidfound = 0, uidnotfound = 0;
Uint8 *frameData = new Uint8[frameSize];

Expand All @@ -137,10 +169,15 @@ namespace dcmqi {

for(size_t segFileNumber=0; segFileNumber<segmentations.size(); segFileNumber++){

vector<vector<int> > slice2derimg = getSliceMapForSegmentation2DerivationImage(dcmDatasets, segmentations[segFileNumber]);
for(vector<vector<int> >::const_iterator vI=slice2derimg.begin();vI!=slice2derimg.end();++vI)
if((*vI).size()>0)
hasDerivationImages = true;
vector<vector<int> > slice2derimg;
if(referencesGeometryCheck){
slice2derimg = getSliceMapForSegmentation2DerivationImage(dcmDatasets, segmentations[segFileNumber]);
for(vector<vector<int> >::const_iterator vI=slice2derimg.begin();vI!=slice2derimg.end();++vI)
if((*vI).size()>0)
hasDerivationImages = true;
} else {
hasDerivationImages = false;
}

perFrameFGs.clear();
perFrameFGs.push_back(fgppp);
Expand Down Expand Up @@ -360,50 +397,52 @@ namespace dcmqi {
}

OFVector<DcmDataset*> siVector;
for(size_t derImageInstanceNum=0;
derImageInstanceNum<slice2derimg[sliceNumber].size();
derImageInstanceNum++){
siVector.push_back(dcmDatasets[slice2derimg[sliceNumber][derImageInstanceNum]]);
}
if(referencesGeometryCheck){
for(size_t derImageInstanceNum=0;
derImageInstanceNum<slice2derimg[sliceNumber].size();
derImageInstanceNum++){
siVector.push_back(dcmDatasets[slice2derimg[sliceNumber][derImageInstanceNum]]);
}

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<SourceImageItem*> 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<SourceImageItem*> 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++;
}
}
}
}

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();
}
Expand All @@ -418,7 +457,8 @@ namespace dcmqi {

delete fgfc;
delete fgppp;
delete fgder;
if(referencesGeometryCheck)
delete fgder;

segdoc->getSeries().setSeriesNumber(metaInfo.getSeriesNumber().c_str());

Expand Down

0 comments on commit 504cc7b

Please sign in to comment.