diff --git a/Modules/DICOMReader/Resources/configurations/3D/imageposition.xml b/Modules/DICOMReader/Resources/configurations/3D/imageposition.xml
index 5307c094f5..6d82712605 100644
--- a/Modules/DICOMReader/Resources/configurations/3D/imageposition.xml
+++ b/Modules/DICOMReader/Resources/configurations/3D/imageposition.xml
@@ -1,25 +1,25 @@
-
+
diff --git a/Modules/DICOMReader/Resources/configurations/3D/imageposition_byacquisition.xml b/Modules/DICOMReader/Resources/configurations/3D/imageposition_byacquisition.xml
index 02309cdfb8..031eb7a34f 100644
--- a/Modules/DICOMReader/Resources/configurations/3D/imageposition_byacquisition.xml
+++ b/Modules/DICOMReader/Resources/configurations/3D/imageposition_byacquisition.xml
@@ -1,26 +1,26 @@
-
+
diff --git a/Modules/DICOMReader/Resources/configurations/3D/instancenumber.xml b/Modules/DICOMReader/Resources/configurations/3D/instancenumber.xml
index e8813cbb00..691e0cee0f 100644
--- a/Modules/DICOMReader/Resources/configurations/3D/instancenumber.xml
+++ b/Modules/DICOMReader/Resources/configurations/3D/instancenumber.xml
@@ -1,24 +1,24 @@
-
+
diff --git a/Modules/DICOMReader/Resources/configurations/3D/instancenumber_soft.xml b/Modules/DICOMReader/Resources/configurations/3D/instancenumber_soft.xml
index fc71950a22..ec9a5a1b00 100644
--- a/Modules/DICOMReader/Resources/configurations/3D/instancenumber_soft.xml
+++ b/Modules/DICOMReader/Resources/configurations/3D/instancenumber_soft.xml
@@ -1,24 +1,24 @@
-
+
diff --git a/Modules/DICOMReader/Resources/configurations/3D/slicelocation.xml b/Modules/DICOMReader/Resources/configurations/3D/slicelocation.xml
index 4023c92764..6ee69fda1b 100644
--- a/Modules/DICOMReader/Resources/configurations/3D/slicelocation.xml
+++ b/Modules/DICOMReader/Resources/configurations/3D/slicelocation.xml
@@ -1,24 +1,24 @@
-
+
diff --git a/Modules/DICOMReader/mitkDICOMReaderConfigurator.cpp b/Modules/DICOMReader/mitkDICOMReaderConfigurator.cpp
index baa0252984..d04279fe30 100644
--- a/Modules/DICOMReader/mitkDICOMReaderConfigurator.cpp
+++ b/Modules/DICOMReader/mitkDICOMReaderConfigurator.cpp
@@ -1,679 +1,690 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "mitkDICOMReaderConfigurator.h"
#include "mitkDICOMSortByTag.h"
#include "mitkSortByImagePositionPatient.h"
mitk::DICOMReaderConfigurator
::DICOMReaderConfigurator()
{
}
mitk::DICOMReaderConfigurator
::~DICOMReaderConfigurator()
{
}
mitk::DICOMFileReader::Pointer
mitk::DICOMReaderConfigurator
::CreateFromConfigFile(const std::string& filename) const
{
TiXmlDocument doc (filename);
if (doc.LoadFile())
{
return this->CreateFromTiXmlDocument( doc );
}
else
{
MITK_ERROR << "Unable to load file at '" << filename <<"'";
return DICOMFileReader::Pointer();
}
}
mitk::DICOMFileReader::Pointer
mitk::DICOMReaderConfigurator
::CreateFromUTF8ConfigString(const std::string& xmlContents) const
{
TiXmlDocument doc;
doc.Parse(xmlContents.c_str(), 0, TIXML_ENCODING_UTF8);
return this->CreateFromTiXmlDocument( doc );
}
mitk::DICOMFileReader::Pointer
mitk::DICOMReaderConfigurator
::CreateFromTiXmlDocument(TiXmlDocument& doc) const
{
TiXmlHandle root(doc.RootElement());
if (TiXmlElement* rootElement = root.ToElement())
{
if (strcmp(rootElement->Value(), "DICOMFileReader")) // :-( no std::string methods
{
MITK_ERROR << "File should contain a tag at top-level! Found '"
<< (rootElement->Value() ? std::string(rootElement->Value()) : std::string("!nothing!")) << "' instead";
return NULL;
}
const char* classnameC = rootElement->Attribute("class");
if (!classnameC)
{
MITK_ERROR << "File should name a reader class in the class attribute: . Found nothing instead";
return NULL;
}
int version(1);
if ( rootElement->QueryIntAttribute("version", &version) == TIXML_SUCCESS)
{
if (version != 1)
{
MITK_WARN << "This reader is only capable of creating DICOMFileReaders of version 1. "
<< "Will not continue, because given configuration is meant for version " << version << ".";
return NULL;
}
}
else
{
MITK_ERROR << "File should name the version of the reader class in the version attribute: ."
<< " Found nothing instead, assuming version 1!";
version = 1;
}
std::string classname(classnameC);
double decimalPlacesForOrientation(5);
bool useDecimalPlacesForOrientation(false);
useDecimalPlacesForOrientation =
rootElement->QueryDoubleAttribute("decimalPlacesForOrientation", &decimalPlacesForOrientation) == TIXML_SUCCESS; // attribute present and a double value
if (classname == "ClassicDICOMSeriesReader")
{
mitk::ClassicDICOMSeriesReader::Pointer reader = mitk::ClassicDICOMSeriesReader::New();
this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader.GetPointer(), rootElement);
this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader.GetPointer(), rootElement);
return reader.GetPointer();
}
if (classname == "ThreeDnTDICOMSeriesReader")
{
mitk::ThreeDnTDICOMSeriesReader::Pointer reader;
if (useDecimalPlacesForOrientation)
reader = mitk::ThreeDnTDICOMSeriesReader::New(decimalPlacesForOrientation);
else
reader = mitk::ThreeDnTDICOMSeriesReader::New();
return ConfigureThreeDnTDICOMSeriesReader(reader, rootElement).GetPointer();
}
else
if (classname == "DICOMITKSeriesGDCMReader")
{
mitk::DICOMITKSeriesGDCMReader::Pointer reader;
if (useDecimalPlacesForOrientation)
reader = mitk::DICOMITKSeriesGDCMReader::New(decimalPlacesForOrientation);
else
reader = mitk::DICOMITKSeriesGDCMReader::New();
return ConfigureDICOMITKSeriesGDCMReader(reader, rootElement).GetPointer();
}
else
{
MITK_ERROR << "DICOMFileReader tag names unknown class '" << classname << "'";
return NULL;
}
}
else
{
MITK_ERROR << "Great confusion: no root element in XML document. Expecting a DICOMFileReader tag at top-level.";
return NULL;
}
}
#define boolStringTrue(s) \
( s == "true" || s == "on" || s == "1" \
|| s == "TRUE" || s == "ON")
void
mitk::DICOMReaderConfigurator
::ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, TiXmlElement* element) const
{
// add the "group3DnT" flag
bool group3DnT(true);
const char* group3DnTC = element->Attribute("group3DnT");
if (group3DnTC)
{
std::string group3DnTS(group3DnTC);
group3DnT = boolStringTrue(group3DnTS);
}
reader->SetGroup3DandT( group3DnT );
}
mitk::ThreeDnTDICOMSeriesReader::Pointer
mitk::DICOMReaderConfigurator
::ConfigureThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, TiXmlElement* element) const
{
assert(element);
// use all the base class configuration
if (this->ConfigureDICOMITKSeriesGDCMReader( reader.GetPointer(), element ).IsNull())
{
return NULL;
}
this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader,element);
return reader;
}
void
mitk::DICOMReaderConfigurator
::ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, TiXmlElement* element) const
{
assert(element);
const char* configLabelC = element->Attribute("label");
if (configLabelC)
{
std::string configLabel(configLabelC);
reader->SetConfigurationLabel(configLabel);
}
const char* configDescriptionC = element->Attribute("description");
if (configDescriptionC)
{
std::string configDescription(configDescriptionC);
reader->SetConfigurationDescription(configDescriptionC);
}
// "fixTiltByShearing" flag
bool fixTiltByShearing(false);
const char* fixTiltByShearingC = element->Attribute("fixTiltByShearing");
if (fixTiltByShearingC)
{
std::string fixTiltByShearingS(fixTiltByShearingC);
fixTiltByShearing = boolStringTrue(fixTiltByShearingS);
}
reader->SetFixTiltByShearing( fixTiltByShearing );
}
mitk::DICOMITKSeriesGDCMReader::Pointer
mitk::DICOMReaderConfigurator
::ConfigureDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, TiXmlElement* element) const
{
assert(element);
this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader, element);
// "acceptTwoSlicesGroups" flag
bool acceptTwoSlicesGroups(true);
const char* acceptTwoSlicesGroupsC = element->Attribute("acceptTwoSlicesGroups");
if (acceptTwoSlicesGroupsC)
{
std::string acceptTwoSlicesGroupsS(acceptTwoSlicesGroupsC);
acceptTwoSlicesGroups = boolStringTrue(acceptTwoSlicesGroupsS);
}
reader->SetAcceptTwoSlicesGroups( acceptTwoSlicesGroups );
// "toleratedOriginError" attribute (double)
bool toleratedOriginErrorIsAbsolute(false);
const char* toleratedOriginErrorIsAbsoluteC = element->Attribute("toleratedOriginErrorIsAbsolute");
if (toleratedOriginErrorIsAbsoluteC)
{
std::string toleratedOriginErrorIsAbsoluteS(toleratedOriginErrorIsAbsoluteC);
toleratedOriginErrorIsAbsolute = boolStringTrue(toleratedOriginErrorIsAbsoluteS);
}
double toleratedOriginError(0.3);
if (element->QueryDoubleAttribute("toleratedOriginError", &toleratedOriginError) == TIXML_SUCCESS) // attribute present and a double value
{
if (toleratedOriginErrorIsAbsolute)
{
reader->SetToleratedOriginOffset( toleratedOriginError );
}
else
{
reader->SetToleratedOriginOffsetToAdaptive( toleratedOriginError );
}
}
// DICOMTagBasedSorters are the only thing we create at this point
// TODO for-loop over all child elements of type DICOMTagBasedSorter, BUT actually a single sorter of this type is enough.
TiXmlElement* dElement = element->FirstChildElement("DICOMDatasetSorter");
if (dElement)
{
const char* classnameC = dElement->Attribute("class");
if (!classnameC)
{
MITK_ERROR << "File should name a DICOMDatasetSorter class in the class attribute of . Found nothing instead";
return NULL;
}
std::string classname(classnameC);
if (classname == "DICOMTagBasedSorter")
{
DICOMTagBasedSorter::Pointer tagSorter = CreateDICOMTagBasedSorter(dElement);
if (tagSorter.IsNotNull())
{
reader->AddSortingElement( tagSorter );
}
}
else
{
MITK_ERROR << "DICOMDatasetSorter tag names unknown class '" << classname << "'";
return NULL;
}
}
return reader;
}
mitk::DICOMTagBasedSorter::Pointer
mitk::DICOMReaderConfigurator
::CreateDICOMTagBasedSorter(TiXmlElement* element) const
{
mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New();
// "strictSorting" parameter!
bool strictSorting(true);
const char* strictSortingC = element->Attribute("strictSorting");
if (strictSortingC)
{
std::string strictSortingS(strictSortingC);
strictSorting = boolStringTrue(strictSortingS);
}
tagSorter->SetStrictSorting(strictSorting);
+ // "strictSorting" parameter!
+ bool expectDistanceOne(true);
+ const char* expectDistanceOneC = element->Attribute("expectDistanceOne");
+ if (expectDistanceOneC)
+ {
+ std::string expectDistanceOneS(expectDistanceOneC);
+ expectDistanceOne = boolStringTrue(expectDistanceOneS);
+ }
+ tagSorter->SetExpectDistanceOne(expectDistanceOne);
+
TiXmlElement* dElement = element->FirstChildElement("Distinguishing");
if (dElement)
{
for ( TiXmlElement* tChild = dElement->FirstChildElement();
tChild != NULL;
tChild = tChild->NextSiblingElement() )
{
try
{
mitk::DICOMTag tag = tagFromXMLElement(tChild);
int i(5);
if (tChild->QueryIntAttribute("cutDecimalPlaces", &i) == TIXML_SUCCESS)
{
tagSorter->AddDistinguishingTag( tag, new mitk::DICOMTagBasedSorter::CutDecimalPlaces(i) );
}
else
{
tagSorter->AddDistinguishingTag( tag );
}
}
catch(...)
{
return NULL;
}
}
}
// "sorting tags"
TiXmlElement* sElement = element->FirstChildElement("Sorting");
if (sElement)
{
DICOMSortCriterion::Pointer previousCriterion;
DICOMSortCriterion::Pointer currentCriterion;
for ( TiXmlNode* tChildNode = sElement->LastChild();
tChildNode != NULL;
tChildNode = tChildNode->PreviousSibling() )
{
TiXmlElement* tChild = tChildNode->ToElement();
if (!tChild) continue;
if (!strcmp(tChild->Value(), "Tag"))
{
try
{
currentCriterion = this->CreateDICOMSortByTag(tChild, previousCriterion);
}
catch(...)
{
std::stringstream ss;
ss << "Could not parse element at (input line " << tChild->Row() << ", col. " << tChild->Column() << ")!";
MITK_ERROR << ss.str();
return NULL;
}
}
else
if (!strcmp(tChild->Value(), "ImagePositionPatient"))
{
try
{
currentCriterion = this->CreateSortByImagePositionPatient(tChild, previousCriterion);
}
catch(...)
{
std::stringstream ss;
ss << "Could not parse element at (input line " << tChild->Row() << ", col. " << tChild->Column() << ")!";
MITK_ERROR << ss.str();
return NULL;
}
}
else
{
MITK_ERROR << "File contain unknown tag <" << tChild->Value() << "> tag as child to ! Cannot interpret...";
}
previousCriterion = currentCriterion;
}
tagSorter->SetSortCriterion( currentCriterion.GetPointer() );
}
return tagSorter;
}
std::string
mitk::DICOMReaderConfigurator
::requiredStringAttribute(TiXmlElement* xmlElement, const std::string& key) const
{
assert(xmlElement);
const char* gC = xmlElement->Attribute(key.c_str());
if (gC)
{
std::string gS(gC);
return gS;
}
else
{
std::stringstream ss;
ss << "Expected an attribute '" << key << "' at this position "
"(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!";
MITK_ERROR << ss.str();
throw std::invalid_argument( ss.str() );
}
}
unsigned int
mitk::DICOMReaderConfigurator
::hexStringToUInt(const std::string& s) const
{
std::stringstream converter(s);
unsigned int ui;
converter >> std::hex >> ui;
MITK_DEBUG << "Converted string '" << s << "' to unsigned int " << ui;
return ui;
}
mitk::DICOMTag
mitk::DICOMReaderConfigurator
::tagFromXMLElement(TiXmlElement* xmlElement) const
{
assert(xmlElement);
if (strcmp(xmlElement->Value(), "Tag")) // :-( no std::string methods
{
std::stringstream ss;
ss << "Expected a tag at this position "
"(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!";
MITK_ERROR << ss.str();
throw std::invalid_argument( ss.str() );
}
std::string name = requiredStringAttribute(xmlElement, "name");
std::string groupS = requiredStringAttribute(xmlElement, "group");
std::string elementS = requiredStringAttribute(xmlElement, "element");
try
{
// convert string to int (assuming string is in hex format with leading "0x" like "0x0020")
unsigned int group = hexStringToUInt(groupS);
unsigned int element = hexStringToUInt(elementS);
return DICOMTag(group, element);
}
catch(...)
{
std::stringstream ss;
ss << "Expected group and element values in to be hexadecimal with leading 0x, e.g. '0x0020'"
"(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!";
MITK_ERROR << ss.str();
throw std::invalid_argument( ss.str() );
}
}
mitk::DICOMSortCriterion::Pointer
mitk::DICOMReaderConfigurator
::CreateDICOMSortByTag(TiXmlElement* xmlElement, DICOMSortCriterion::Pointer secondaryCriterion) const
{
mitk::DICOMTag tag = tagFromXMLElement(xmlElement);
return DICOMSortByTag::New(tag, secondaryCriterion).GetPointer();
}
mitk::DICOMSortCriterion::Pointer
mitk::DICOMReaderConfigurator
::CreateSortByImagePositionPatient(TiXmlElement*, DICOMSortCriterion::Pointer secondaryCriterion) const
{
return SortByImagePositionPatient::New(secondaryCriterion).GetPointer();
}
std::string
mitk::DICOMReaderConfigurator
::CreateConfigStringFromReader(DICOMFileReader::ConstPointer reader) const
{
// check possible sub-classes from the most-specific one up to the most generic one
const DICOMFileReader* cPointer = reader;
TiXmlElement* root;
if (const ClassicDICOMSeriesReader* specificReader = dynamic_cast(cPointer))
{
root = this->CreateConfigStringFromReader(specificReader);
}
else
if (const ThreeDnTDICOMSeriesReader* specificReader = dynamic_cast(cPointer))
{
root = this->CreateConfigStringFromReader(specificReader);
}
else
if (const DICOMITKSeriesGDCMReader* specificReader = dynamic_cast(cPointer))
{
root = this->CreateConfigStringFromReader(specificReader);
}
else
{
MITK_WARN << "Unknown reader class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize.";
return ""; // no serialization, what a pity
}
if (root)
{
TiXmlDocument document;
document.LinkEndChild( root );
TiXmlPrinter printer;
printer.SetIndent( " " );
document.Accept( &printer );
std::string xmltext = printer.CStr();
return xmltext;
}
else
{
MITK_WARN << "DICOMReaderConfigurator::CreateConfigStringFromReader() created empty serialization. Problem?";
return "";
}
}
TiXmlElement*
mitk::DICOMReaderConfigurator
::CreateConfigStringFromReader(const DICOMITKSeriesGDCMReader* reader) const
{
TiXmlElement* root = this->CreateDICOMFileReaderTag(reader);
assert(root);
root->SetAttribute("fixTiltByShearing", toString(reader->GetFixTiltByShearing()));
root->SetAttribute("acceptTwoSlicesGroups", toString(reader->GetAcceptTwoSlicesGroups()));
root->SetDoubleAttribute("toleratedOriginError", reader->GetToleratedOriginError());
root->SetAttribute("toleratedOriginErrorIsAbsolute", toString(reader->IsToleratedOriginOffsetAbsolute()));
root->SetDoubleAttribute("decimalPlacesForOrientation", reader->GetDecimalPlacesForOrientation());
// iterate DICOMDatasetSorter objects
DICOMITKSeriesGDCMReader::ConstSorterList sorterList = reader->GetFreelyConfiguredSortingElements();
for(DICOMITKSeriesGDCMReader::ConstSorterList::const_iterator sorterIter = sorterList.begin();
sorterIter != sorterList.end();
++sorterIter)
{
const DICOMDatasetSorter* sorter = *sorterIter;
if (const DICOMTagBasedSorter* specificSorter = dynamic_cast(sorter))
{
TiXmlElement* sorterTag = this->CreateConfigStringFromDICOMDatasetSorter(specificSorter);
root->LinkEndChild(sorterTag);
}
else
{
MITK_WARN << "Unknown DICOMDatasetSorter class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize.";
return NULL;
}
}
return root;
}
TiXmlElement*
mitk::DICOMReaderConfigurator
::CreateConfigStringFromDICOMDatasetSorter(const DICOMTagBasedSorter* sorter) const
{
assert(sorter);
TiXmlElement* sorterTag = new TiXmlElement("DICOMDatasetSorter");
sorterTag->SetAttribute("class", sorter->GetNameOfClass());
sorterTag->SetAttribute("strictSorting", toString(sorter->GetStrictSorting()));
+ sorterTag->SetAttribute("expectDistanceOne", toString(sorter->GetExpectDistanceOne()));
TiXmlElement* distinguishingTagsElement = new TiXmlElement("Distinguishing");
sorterTag->LinkEndChild(distinguishingTagsElement);
mitk::DICOMTagList distinguishingTags = sorter->GetDistinguishingTags();
for (DICOMTagList::iterator tagIter = distinguishingTags.begin();
tagIter != distinguishingTags.end();
++tagIter)
{
TiXmlElement* tag = this->CreateConfigStringFromDICOMTag(*tagIter);
distinguishingTagsElement->LinkEndChild(tag);
const DICOMTagBasedSorter::TagValueProcessor* processor = sorter->GetTagValueProcessorForDistinguishingTag(*tagIter);
if (const DICOMTagBasedSorter::CutDecimalPlaces* specificProcessor = dynamic_cast(processor))
{
tag->SetDoubleAttribute("cutDecimalPlaces", specificProcessor->GetPrecision());
}
}
TiXmlElement* sortingElement = new TiXmlElement("Sorting");
sorterTag->LinkEndChild(sortingElement);
mitk::DICOMSortCriterion::ConstPointer sortCriterion = sorter->GetSortCriterion();
while (sortCriterion.IsNotNull())
{
std::string classname = sortCriterion->GetNameOfClass();
if (classname == "SortByImagePositionPatient")
{
sortingElement->LinkEndChild( new TiXmlElement("ImagePositionPatient") ); // no parameters
}
else
if (classname == "DICOMSortByTag")
{
DICOMTagList pseudoTagList = sortCriterion->GetTagsOfInterest();
if (pseudoTagList.size() == 1)
{
DICOMTag firstTag = pseudoTagList.front();
TiXmlElement* tagElement = this->CreateConfigStringFromDICOMTag(firstTag);
sortingElement->LinkEndChild( tagElement );
}
else
{
MITK_ERROR << "Encountered SortByTag class with MULTIPLE tag in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize.";
return NULL;
}
}
else
{
MITK_ERROR << "Encountered unknown class '" << classname << "' in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize.";
return NULL;
}
sortCriterion = sortCriterion->GetSecondaryCriterion();
}
return sorterTag;
}
TiXmlElement*
mitk::DICOMReaderConfigurator
::CreateConfigStringFromDICOMTag(const DICOMTag& tag) const
{
TiXmlElement* tagElement = new TiXmlElement("Tag"); // name group element
tagElement->SetAttribute("name", tag.GetName().c_str());
tagElement->SetAttribute("group", toHexString(tag.GetGroup()));
tagElement->SetAttribute("element", toHexString(tag.GetElement()));
return tagElement;
}
std::string
mitk::DICOMReaderConfigurator
::toHexString(unsigned int i) const
{
std::stringstream ss;
ss << "0x" << std::setfill ('0') << std::setw(4) << std::hex << i;
return ss.str();
}
TiXmlElement*
mitk::DICOMReaderConfigurator
::CreateConfigStringFromReader(const ThreeDnTDICOMSeriesReader* reader) const
{
TiXmlElement* root = this->CreateConfigStringFromReader(static_cast(reader));
assert(root);
root->SetAttribute("group3DnT", toString(reader->GetGroup3DandT()));
return root;
}
const char*
mitk::DICOMReaderConfigurator
::toString(bool b) const
{
return b ? "true" : "false";
}
TiXmlElement*
mitk::DICOMReaderConfigurator
::CreateConfigStringFromReader(const ClassicDICOMSeriesReader* reader) const
{
return this->CreateDICOMFileReaderTag(reader);
}
TiXmlElement*
mitk::DICOMReaderConfigurator
::CreateDICOMFileReaderTag(const DICOMFileReader* reader) const
{
TiXmlElement* readerTag = new TiXmlElement("DICOMFileReader");
readerTag->SetAttribute("class", reader->GetNameOfClass());
readerTag->SetAttribute("label", reader->GetConfigurationLabel().c_str());
readerTag->SetAttribute("description", reader->GetConfigurationDescription().c_str());
readerTag->SetAttribute("version", "1");
return readerTag;
}
diff --git a/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp b/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp
index 34f4b8fac9..3d6937b55b 100644
--- a/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp
+++ b/Modules/DICOMReader/mitkDICOMTagBasedSorter.cpp
@@ -1,559 +1,589 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "mitkDICOMTagBasedSorter.h"
#include
#include
mitk::DICOMTagBasedSorter::CutDecimalPlaces
::CutDecimalPlaces(unsigned int precision)
:m_Precision(precision)
{
}
mitk::DICOMTagBasedSorter::CutDecimalPlaces
::CutDecimalPlaces(const CutDecimalPlaces& other)
:m_Precision(other.m_Precision)
{
}
std::string
mitk::DICOMTagBasedSorter::CutDecimalPlaces
::operator()(const std::string& input) const
{
// be a bit tolerant for tags such as image orientation orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263)
// iterate all fields, convert each to a number, cut this number as configured, then return a concatenated string with all cut-off numbers
std::ostringstream resultString;
resultString.str(std::string());
resultString.clear();
resultString.setf(std::ios::fixed, std::ios::floatfield);
resultString.precision(m_Precision);
std::stringstream ss(input);
ss.str(input);
ss.clear();
std::string item;
double number(0);
std::istringstream converter(item);
while (std::getline(ss, item, '\\'))
{
converter.str(item);
converter.clear();
if (converter >> number && converter.eof())
{
// converted to double
resultString << number;
}
else
{
// did not convert to double
resultString << item; // just paste the unmodified string
}
if (!ss.eof())
{
resultString << "\\";
}
}
return resultString.str();
}
mitk::DICOMTagBasedSorter::TagValueProcessor*
mitk::DICOMTagBasedSorter::CutDecimalPlaces
::Clone() const
{
return new CutDecimalPlaces(*this);
}
unsigned int
mitk::DICOMTagBasedSorter::CutDecimalPlaces
::GetPrecision() const
{
return m_Precision;
}
mitk::DICOMTagBasedSorter
::DICOMTagBasedSorter()
:DICOMDatasetSorter()
-,m_StrictSorting(true)
+,m_StrictSorting(false)
+,m_ExpectDistanceOne(false)
{
}
mitk::DICOMTagBasedSorter
::~DICOMTagBasedSorter()
{
for(TagValueProcessorMap::iterator ti = m_TagValueProcessor.begin();
ti != m_TagValueProcessor.end();
++ti)
{
delete ti->second;
}
}
mitk::DICOMTagBasedSorter
::DICOMTagBasedSorter(const DICOMTagBasedSorter& other )
:DICOMDatasetSorter(other)
,m_DistinguishingTags( other.m_DistinguishingTags )
,m_SortCriterion( other.m_SortCriterion )
,m_StrictSorting( other.m_StrictSorting )
+,m_ExpectDistanceOne( other.m_ExpectDistanceOne )
{
for(TagValueProcessorMap::const_iterator ti = other.m_TagValueProcessor.begin();
ti != other.m_TagValueProcessor.end();
++ti)
{
m_TagValueProcessor[ti->first] = ti->second->Clone();
}
}
mitk::DICOMTagBasedSorter&
mitk::DICOMTagBasedSorter
::operator=(const DICOMTagBasedSorter& other)
{
if (this != &other)
{
DICOMDatasetSorter::operator=(other);
m_DistinguishingTags = other.m_DistinguishingTags;
m_SortCriterion = other.m_SortCriterion;
m_StrictSorting = other.m_StrictSorting;
+ m_ExpectDistanceOne = other.m_ExpectDistanceOne;
for(TagValueProcessorMap::const_iterator ti = other.m_TagValueProcessor.begin();
ti != other.m_TagValueProcessor.end();
++ti)
{
m_TagValueProcessor[ti->first] = ti->second->Clone();
}
}
return *this;
}
bool
mitk::DICOMTagBasedSorter
::operator==(const DICOMDatasetSorter& other) const
{
if (const DICOMTagBasedSorter* otherSelf = dynamic_cast(&other))
{
if (this->m_StrictSorting != otherSelf->m_StrictSorting) return false;
+ if (this->m_ExpectDistanceOne != otherSelf->m_ExpectDistanceOne) return false;
bool allTagsPresentAndEqual(true);
if (this->m_DistinguishingTags.size() != otherSelf->m_DistinguishingTags.size())
return false;
for (DICOMTagList::const_iterator myTag = this->m_DistinguishingTags.begin();
myTag != this->m_DistinguishingTags.end();
++myTag)
{
allTagsPresentAndEqual &= (std::find( otherSelf->m_DistinguishingTags.begin(), otherSelf->m_DistinguishingTags.end(), *myTag )
!= otherSelf->m_DistinguishingTags.end()); // other contains this tags
// since size is equal, we don't need to check the inverse
}
if (!allTagsPresentAndEqual) return false;
if (this->m_SortCriterion.IsNotNull() && otherSelf->m_SortCriterion.IsNotNull())
{
return *(this->m_SortCriterion) == *(otherSelf->m_SortCriterion);
}
else
{
return this->m_SortCriterion.IsNull() && otherSelf->m_SortCriterion.IsNull();
}
}
else
{
return false;
}
}
void
mitk::DICOMTagBasedSorter
::PrintConfiguration(std::ostream& os, const std::string& indent) const
{
- os << indent << "Tag based sorting:" << std::endl;
+ os << indent << "Tag based sorting "
+ << "(strict=" << (m_StrictSorting?"true":"false")
+ << ", expectDistanceOne=" << (m_ExpectDistanceOne?"true":"false") << "):"
+ << std::endl;
+
for (DICOMTagList::const_iterator tagIter = m_DistinguishingTags.begin();
tagIter != m_DistinguishingTags.end();
++tagIter)
{
os << indent << " Split on ";
tagIter->Print(os);
os << std::endl;
}
DICOMSortCriterion::ConstPointer crit = m_SortCriterion.GetPointer();
while (crit.IsNotNull())
{
os << indent << " Sort by ";
crit->Print(os);
os << std::endl;
crit = crit->GetSecondaryCriterion();
}
}
void
mitk::DICOMTagBasedSorter
::SetStrictSorting(bool strict)
{
m_StrictSorting = strict;
}
bool
mitk::DICOMTagBasedSorter
::GetStrictSorting() const
{
return m_StrictSorting;
}
+void
+mitk::DICOMTagBasedSorter
+::SetExpectDistanceOne(bool strict)
+{
+ m_ExpectDistanceOne = strict;
+}
+
+bool
+mitk::DICOMTagBasedSorter
+::GetExpectDistanceOne() const
+{
+ return m_ExpectDistanceOne;
+}
+
+
mitk::DICOMTagList
mitk::DICOMTagBasedSorter
::GetTagsOfInterest()
{
DICOMTagList allTags = m_DistinguishingTags;
DICOMTagList sortingRelevantTags = m_SortCriterion->GetAllTagsOfInterest();
allTags.insert( allTags.end(), sortingRelevantTags.begin(), sortingRelevantTags.end() ); // append
return allTags;
}
mitk::DICOMTagList
mitk::DICOMTagBasedSorter
::GetDistinguishingTags() const
{
return m_DistinguishingTags;
}
const mitk::DICOMTagBasedSorter::TagValueProcessor*
mitk::DICOMTagBasedSorter
::GetTagValueProcessorForDistinguishingTag(const DICOMTag& tag) const
{
TagValueProcessorMap::const_iterator loc = m_TagValueProcessor.find(tag);
if (loc != m_TagValueProcessor.end())
{
return loc->second;
}
else
{
return NULL;
}
}
void
mitk::DICOMTagBasedSorter
::AddDistinguishingTag( const DICOMTag& tag, TagValueProcessor* tagValueProcessor )
{
m_DistinguishingTags.push_back(tag);
m_TagValueProcessor[tag] = tagValueProcessor;
}
void
mitk::DICOMTagBasedSorter
::SetSortCriterion( DICOMSortCriterion::ConstPointer criterion )
{
m_SortCriterion = criterion;
}
mitk::DICOMSortCriterion::ConstPointer
mitk::DICOMTagBasedSorter
::GetSortCriterion() const
{
return m_SortCriterion;
}
void
mitk::DICOMTagBasedSorter
::Sort()
{
// 1. split
// 2. sort each group
GroupIDToListType groups = this->SplitInputGroups();
GroupIDToListType& sortedGroups = this->SortGroups( groups );
// 3. define output
this->SetNumberOfOutputs(sortedGroups.size());
unsigned int outputIndex(0);
for (GroupIDToListType::iterator groupIter = sortedGroups.begin();
groupIter != sortedGroups.end();
++outputIndex, ++groupIter)
{
this->SetOutput(outputIndex, groupIter->second);
}
}
std::string
mitk::DICOMTagBasedSorter
::BuildGroupID( DICOMDatasetAccess* dataset )
{
// just concatenate all tag values
assert(dataset);
std::stringstream groupID;
groupID << "g";
for (DICOMTagList::iterator tagIter = m_DistinguishingTags.begin();
tagIter != m_DistinguishingTags.end();
++tagIter)
{
groupID << tagIter->GetGroup() << tagIter->GetElement(); // make group/element part of the id to cover empty tags
std::string rawTagValue = dataset->GetTagValueAsString(*tagIter);
std::string processedTagValue;
if ( m_TagValueProcessor[*tagIter] != NULL )
{
processedTagValue = (*m_TagValueProcessor[*tagIter])(rawTagValue);
}
else
{
processedTagValue = rawTagValue;
}
groupID << processedTagValue;
}
// shorten ID?
return groupID.str();
}
mitk::DICOMTagBasedSorter::GroupIDToListType
mitk::DICOMTagBasedSorter
::SplitInputGroups()
{
DICOMDatasetList input = GetInput(); // copy
GroupIDToListType listForGroupID;
for (DICOMDatasetList::iterator dsIter = input.begin();
dsIter != input.end();
++dsIter)
{
DICOMDatasetAccess* dataset = *dsIter;
assert(dataset);
std::string groupID = this->BuildGroupID( dataset );
MITK_DEBUG << "Group ID for for " << dataset->GetFilenameIfAvailable() << ": " << groupID;
listForGroupID[groupID].push_back(dataset);
}
MITK_DEBUG << "After tag based splitting: " << listForGroupID.size() << " groups";
return listForGroupID;
}
mitk::DICOMTagBasedSorter::GroupIDToListType&
mitk::DICOMTagBasedSorter
::SortGroups(GroupIDToListType& groups)
{
if (m_SortCriterion.IsNotNull())
{
/*
Three steps here:
1. sort within each group
- this may result in orders such as 1 2 3 4 6 7 8 10 12 13 14
2. create new groups by enforcing consecutive order within each group
- resorts above example like 1 2 3 4 ; 6 7 8 ; 10 ; 12 13 14
3. sort all of the groups (not WITHIN each group) by their first frame
- if earlier "distinguish" steps created groups like 6 7 8 ; 1 2 3 4 ; 10,
then this step would sort them like 1 2 3 4 ; 6 7 8 ; 10
*/
// Step 1: sort within the groups
// for each output
// sort by all configured tags, use secondary tags when equal or empty
// make configurable:
// - sorting order (ascending, descending)
// - sort numerically
// - ... ?
unsigned int groupIndex(0);
for (GroupIDToListType::iterator gIter = groups.begin();
gIter != groups.end();
++groupIndex, ++gIter)
{
DICOMDatasetList& dsList = gIter->second;
MITK_DEBUG << " --------------------------------------------------------------------------------";
MITK_DEBUG << " DICOMTagBasedSorter before sorting group : " << groupIndex;
for (DICOMDatasetList::iterator oi = dsList.begin();
oi != dsList.end();
++oi)
{
MITK_DEBUG << " INPUT : " << (*oi)->GetFilenameIfAvailable();
}
std::sort( dsList.begin(), dsList.end(), ParameterizedDatasetSort( m_SortCriterion ) );
MITK_DEBUG << " --------------------------------------------------------------------------------";
MITK_DEBUG << " DICOMTagBasedSorter after sorting group : " << groupIndex;
for (DICOMDatasetList::iterator oi = dsList.begin();
oi != dsList.end();
++oi)
{
MITK_DEBUG << " OUTPUT : " << (*oi)->GetFilenameIfAvailable();
}
MITK_DEBUG << " --------------------------------------------------------------------------------";
}
GroupIDToListType consecutiveGroups;
if (m_StrictSorting)
{
// Step 2: create new groups by enforcing consecutive order within each group
unsigned int groupIndex(0);
for (GroupIDToListType::iterator gIter = groups.begin();
gIter != groups.end();
++gIter)
{
std::stringstream groupKey;
groupKey << std::setfill('0') << std::setw(6) << groupIndex++;
DICOMDatasetList& dsList = gIter->second;
DICOMDatasetAccess* previousDS(NULL);
unsigned int dsIndex(0);
double constantDistance(0.0);
bool constantDistanceInitialized(false);
for (DICOMDatasetList::iterator dataset = dsList.begin();
dataset != dsList.end();
++dsIndex, ++dataset)
{
if (dsIndex >0) // ignore the first dataset, we cannot check any distances yet..
{
// for the second and every following dataset:
// let the sorting criterion calculate a "distance"
// if the distance is not 1, split off a new group!
double currentDistance = m_SortCriterion->NumericDistance(previousDS, *dataset);
if (constantDistanceInitialized)
{
if (fabs(currentDistance - constantDistance) < fabs(constantDistance * 0.01)) // ok, deviation of up to 1% of distance is tolerated
{
// nothing to do, just ok
MITK_DEBUG << "Checking currentDistance==" << currentDistance << ": small enough";
}
//else if (currentDistance < mitk::eps) // close enough to 0
else
{
MITK_DEBUG << "Split consecutive group at index " << dsIndex << " (current distance " << currentDistance << ", constant distance " << constantDistance << ")";
// split! this is done by simply creating a new group (key)
groupKey.str(std::string());
groupKey.clear();
groupKey << std::setfill('0') << std::setw(6) << groupIndex++;
}
}
else
{
// second slice: learn about the expected distance!
// heuristic: if distance is an integer, we check for a special case:
// if the distance is integer and not 1/-1, then we assume
// a missing slice right after the first slice
// ==> split off slices
// in all other cases: second dataset at this position, no need to split already, we are still learning about the images
- if ((currentDistance - (int)currentDistance == 0.0) && fabs(currentDistance) != 1.0)
- // exact comparison. An integer should not be expressed as 1.000000000000000000000000001!
+
+ // addition to the above: when sorting by imagepositions, a distance other than 1 between the first two slices is
+ // not unusual, actually expected... then we should not split
+
+ if (m_ExpectDistanceOne)
{
- MITK_DEBUG << "Split consecutive group at index " << dsIndex << " (strange special case)";
- groupKey.str(std::string());
- groupKey.clear();
- groupKey << std::setfill('0') << std::setw(6) << groupIndex++;
+ if ((currentDistance - (int)currentDistance == 0.0) && fabs(currentDistance) != 1.0)
+ // exact comparison. An integer should not be expressed as 1.000000000000000000000000001!
+ {
+ MITK_DEBUG << "Split consecutive group at index " << dsIndex << " (special case: expected distance 1 exactly)";
+ groupKey.str(std::string());
+ groupKey.clear();
+ groupKey << std::setfill('0') << std::setw(6) << groupIndex++;
+ }
}
MITK_DEBUG << "Initialize strict distance to currentDistance=" << currentDistance;
constantDistance = currentDistance;
constantDistanceInitialized = true;
}
}
consecutiveGroups[groupKey.str()].push_back(*dataset);
previousDS = *dataset;
}
}
}
else
{
consecutiveGroups = groups;
}
// Step 3: sort all of the groups (not WITHIN each group) by their first frame
/*
build a list-1 of datasets with the first dataset one of each group
sort this list-1
build a new result list-2:
- iterate list-1, for each dataset
- find the group that contains this dataset
- add this group as the next element to list-2
return list-2 as the sorted output
*/
DICOMDatasetList firstSlices;
for (GroupIDToListType::iterator gIter = consecutiveGroups.begin();
gIter != consecutiveGroups.end();
++gIter)
{
assert(!gIter->second.empty());
firstSlices.push_back(gIter->second.front());
}
std::sort( firstSlices.begin(), firstSlices.end(), ParameterizedDatasetSort( m_SortCriterion ) );
GroupIDToListType sortedResultBlocks;
unsigned int groupKeyValue(0);
for (DICOMDatasetList::iterator firstSlice = firstSlices.begin();
firstSlice != firstSlices.end();
++firstSlice)
{
for (GroupIDToListType::iterator gIter = consecutiveGroups.begin();
gIter != consecutiveGroups.end();
++groupKeyValue, ++gIter)
{
if (gIter->second.front() == *firstSlice)
{
std::stringstream groupKey;
groupKey << std::setfill('0') << std::setw(6) << groupKeyValue; // try more than 999,999 groups and you are doomed (your application already is)
sortedResultBlocks[groupKey.str()] = gIter->second;
}
}
}
groups = sortedResultBlocks;
}
unsigned int groupIndex(0);
for (GroupIDToListType::iterator gIter = groups.begin();
gIter != groups.end();
++groupIndex, ++gIter)
{
DICOMDatasetList& dsList = gIter->second;
MITK_DEBUG << " --------------------------------------------------------------------------------";
MITK_DEBUG << " DICOMTagBasedSorter after sorting group : " << groupIndex;
for (DICOMDatasetList::iterator oi = dsList.begin();
oi != dsList.end();
++oi)
{
MITK_DEBUG << " OUTPUT : " << (*oi)->GetFilenameIfAvailable();
}
MITK_DEBUG << " --------------------------------------------------------------------------------";
}
return groups;
}
mitk::DICOMTagBasedSorter::ParameterizedDatasetSort
::ParameterizedDatasetSort(DICOMSortCriterion::ConstPointer criterion)
:m_SortCriterion(criterion)
{
}
bool
mitk::DICOMTagBasedSorter::ParameterizedDatasetSort
::operator() (const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right)
{
assert(left);
assert(right);
assert(m_SortCriterion.IsNotNull());
return m_SortCriterion->IsLeftBeforeRight(left, right);
}
diff --git a/Modules/DICOMReader/mitkDICOMTagBasedSorter.h b/Modules/DICOMReader/mitkDICOMTagBasedSorter.h
index 603f2b51a9..5e5600aa0a 100644
--- a/Modules/DICOMReader/mitkDICOMTagBasedSorter.h
+++ b/Modules/DICOMReader/mitkDICOMTagBasedSorter.h
@@ -1,165 +1,191 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#ifndef mitkDICOMTagBasedSorter_h
#define mitkDICOMTagBasedSorter_h
#include "mitkDICOMDatasetSorter.h"
#include "mitkDICOMSortCriterion.h"
namespace mitk
{
/**
\ingroup DICOMReaderModule
\brief Sort DICOM datasets based on configurable tags.
This class implements sorting of input DICOM datasets into multiple outputs
as described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy.
The logic of sorting and splitting is most simple and most generic:
1. Datasets will be put into different groups, if they differ in their value of specific tags (defined by AddDistinguishingTag())
- there might be multiple distinguishing tags defined
- tag values might be processed before comparison by means of TagValueProcessor (e.g. round to a number of decimal places)
2. Each of the groups will be sorted by comparing their tag values using multiple DICOMSortCriterion
- DICOMSortCriterion might evaluate a single tag (e.g. Instance Number) or multiple values (as in SortByImagePositionPatient)
- only a single DICOMSortCriterion is defined for DICOMTagBasedSorter, because each DICOMSortCriterion holds a "secondary sort criterion", i.e. an application can define multiple tags for sorting by chaining \link DICOMSortCriterion DICOMSortCriteria \endlink
- applications should make sure that sorting is always defined (to avoid problems with standard containers), e.g. by adding a comparison of filenames or instance UIDs as a last sorting fallback.
*/
class DICOMReader_EXPORT DICOMTagBasedSorter : public DICOMDatasetSorter
{
public:
/**
\brief Processes tag values before they are compared.
These classes could do some kind of normalization such as rounding, lower case formatting, etc.
*/
class DICOMReader_EXPORT TagValueProcessor
{
public:
/// \brief Implements the "processing".
virtual std::string operator()(const std::string&) const = 0;
virtual TagValueProcessor* Clone() const = 0;
};
/**
\brief Cuts a number after configured number of decimal places.
An instance of this class can be used to avoid errors when comparing minimally different image orientations.
*/
class DICOMReader_EXPORT CutDecimalPlaces : public TagValueProcessor
{
public:
CutDecimalPlaces(unsigned int precision);
CutDecimalPlaces(const CutDecimalPlaces& other);
unsigned int GetPrecision() const;
virtual std::string operator()(const std::string&) const;
virtual TagValueProcessor* Clone() const;
private:
unsigned int m_Precision;
};
mitkClassMacro( DICOMTagBasedSorter, DICOMDatasetSorter )
itkNewMacro( DICOMTagBasedSorter )
/**
\brief Datasets that differ in given tag's value will be sorted into separate outputs.
*/
void AddDistinguishingTag( const DICOMTag&, TagValueProcessor* tagValueProcessor = NULL );
DICOMTagList GetDistinguishingTags() const;
const TagValueProcessor* GetTagValueProcessorForDistinguishingTag(const DICOMTag&) const;
/**
\brief Define the sorting criterion (which holds seconardy criteria)
*/
void SetSortCriterion( DICOMSortCriterion::ConstPointer criterion );
DICOMSortCriterion::ConstPointer GetSortCriterion() const;
/**
\brief A list of all the tags needed for processing (facilitates scanning).
*/
virtual DICOMTagList GetTagsOfInterest();
/**
\brief Whether or not groups should be checked for consecutive tag values.
+
+ When this flag is set (default in constructor=off), the sorter will
+ not only sort in a way that the values of a configured tag are ascending
+ BUT in addition the sorter will enforce a constant numerical distance
+ between values.
+
+ Having this flag is useful for handling of series with missing slices,
+ e.g. Instance Numbers 1 2 3 5 6 7 8. With the flag set to true, the sorter
+ would split this group into two, because the initial distance of 1 is
+ not kept between Instance Numbers 3 and 5.
+
+ A special case of this behavior can be configured by SetExpectDistanceOne().
+ When this additional flag is set to true, the sorter will expect distance
+ 1 exactly. This can help if the second slice is missing already. Without
+ this additional flag, we would "learn" about a wrong distance of 2 (or similar)
+ and then sort completely wrong.
*/
void SetStrictSorting(bool strict);
bool GetStrictSorting() const;
+ /**
+ \brief Flag for a special case in "strict sorting".
+ Please see documentation of SetStrictSorting().
+ \sa SetStrictSorting
+ */
+ void SetExpectDistanceOne(bool strict);
+ bool GetExpectDistanceOne() const;
+
+
/**
\brief Actually sort as described in the Detailed Description.
*/
virtual void Sort();
/**
\brief Print configuration details into given stream.
*/
virtual void PrintConfiguration(std::ostream& os, const std::string& indent = "") const;
virtual bool operator==(const DICOMDatasetSorter& other) const;
protected:
/**
\brief Helper struct to feed into std::sort, configured via DICOMSortCriterion.
*/
struct ParameterizedDatasetSort
{
ParameterizedDatasetSort(DICOMSortCriterion::ConstPointer);
bool operator() (const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right);
DICOMSortCriterion::ConstPointer m_SortCriterion;
};
DICOMTagBasedSorter();
virtual ~DICOMTagBasedSorter();
DICOMTagBasedSorter(const DICOMTagBasedSorter& other);
DICOMTagBasedSorter& operator=(const DICOMTagBasedSorter& other);
/**
\brief Helper for SplitInputGroups().
*/
std::string BuildGroupID( DICOMDatasetAccess* dataset );
typedef std::map GroupIDToListType;
/**
\brief Implements the "distiguishing tags".
To sort datasets into different groups, a long string will be built for each dataset. The string concatenates all tags and their respective values.
Datasets that match in all values will end up with the same string.
*/
GroupIDToListType SplitInputGroups();
/**
\brief Implements the sorting step.
Relatively simple implementation thanks to std::sort and a parameterization via DICOMSortCriterion.
*/
GroupIDToListType& SortGroups(GroupIDToListType& groups);
DICOMTagList m_DistinguishingTags;
typedef std::map TagValueProcessorMap;
TagValueProcessorMap m_TagValueProcessor;
DICOMSortCriterion::ConstPointer m_SortCriterion;
bool m_StrictSorting;
+ bool m_ExpectDistanceOne;
};
}
#endif