diff --git a/Modules/DICOMReader/Testing/mitkDICOMNullFileReader.h b/Modules/DICOMReader/Testing/mitkDICOMNullFileReader.h
index 3b4ad007ff..e276813bf1 100644
--- a/Modules/DICOMReader/Testing/mitkDICOMNullFileReader.h
+++ b/Modules/DICOMReader/Testing/mitkDICOMNullFileReader.h
@@ -1,57 +1,60 @@
/*===================================================================
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 mitkDICOMNullFileReader_h
#define mitkDICOMNullFileReader_h
#include "mitkDICOMFileReader.h"
namespace mitk
{
class DICOMNullFileReader : public DICOMFileReader
{
public:
mitkClassMacro( DICOMNullFileReader, DICOMFileReader );
mitkCloneMacro( DICOMNullFileReader );
itkNewMacro( DICOMNullFileReader );
virtual void AnalyzeInputFiles();
// void AllocateOutputImages();
virtual bool LoadImages();
virtual bool CanHandleFile(const std::string& filename);
bool operator==(const DICOMFileReader& other) const;
+ virtual DICOMTagList GetTagsOfInterest() const { return DICOMTagList(); }
+ virtual void SetTagCache( DICOMTagCache::Pointer ) {}
+
protected:
DICOMNullFileReader();
virtual ~DICOMNullFileReader();
DICOMNullFileReader(const DICOMNullFileReader& other);
DICOMNullFileReader& operator=(const DICOMNullFileReader& other);
void InternalPrintConfiguration(std::ostream& os) const;
private:
};
}
#endif
diff --git a/Modules/DICOMReader/mitkDICOMFileReader.h b/Modules/DICOMReader/mitkDICOMFileReader.h
index 5ea8d9550c..cbd3064f19 100644
--- a/Modules/DICOMReader/mitkDICOMFileReader.h
+++ b/Modules/DICOMReader/mitkDICOMFileReader.h
@@ -1,128 +1,135 @@
/*===================================================================
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 mitkDICOMFileReader_h
#define mitkDICOMFileReader_h
#include "itkObjectFactory.h"
#include "mitkCommon.h"
#include "DICOMReaderExports.h"
+#include "mitkDICOMTagCache.h"
+
#include "mitkDICOMImageBlockDescriptor.h"
namespace mitk
{
// TODO Philips3D!
// TODO http://bugs.mitk.org/show_bug.cgi?id=11572 ?
/**
\ingroup DICOMReaderModule
\brief Interface for DICOM readers that produce mitk::Images.
As described in \ref DICOMReaderModule, this class structures
the reader's part in the process of analyzing a set of DICOM files
and selecting the most appropriate reader.
The overall loading process is as follows:
- Define input files: a list of absolute filenames
- Analyze the potential output: see what can be made of the input files, describe with DICOMImageBlockDescriptor%s
- Load pixel data: an application will usually analyze files using multiple readers and only load with a single reader
Sub-classes are required to implement a number of methods that
reflect above structure. See mitk::DICOMITKSeriesGDCMReader for
an example.
To help applications in describing different readers to the user, each reader
brings a number of methods that describe its configuration/specifics by
means of a short label and a (longer) description.
*/
class DICOMReader_EXPORT DICOMFileReader : virtual public itk::Object
{
public:
mitkClassMacro( DICOMFileReader, itk::Object );
/// Test whether a file is DICOM at all
static bool IsDICOM(const std::string& filename);
/// Indicate whether this reader can handle given file
virtual bool CanHandleFile(const std::string& filename) = 0;
/// This input files
void SetInputFiles(StringList filenames);
/// This input files
const StringList& GetInputFiles() const;
/// Analyze input files
virtual void AnalyzeInputFiles() = 0;
/// Number of outputs, only meaningful after calling AnalyzeInputFiles()
unsigned int GetNumberOfOutputs() const;
/// Individual outputs, only meaningful after calling AnalyzeInputFiles(). \throws std::invalid_argument
const DICOMImageBlockDescriptor& GetOutput(unsigned int index) const;
// void AllocateOutputImages(); TODO for later implementation of slice-by-slice loading
/// Load the mitk::Image%s in our outputs, the DICOMImageBlockDescriptor. To be called only after AnalyzeInputFiles(). Take care of potential exceptions!
virtual bool LoadImages() = 0;
+ // TODO seems reasonable
+ virtual DICOMTagList GetTagsOfInterest() const = 0;
+ // TODO need to document
+ virtual void SetTagCache(DICOMTagCache::Pointer) = 0;
+
/// Short label/name to describe this reader
void SetConfigurationLabel(const std::string&);
/// Short label/name to describe this reader
std::string GetConfigurationLabel() const;
/// One-sentence description of the reader's loading "strategy"
void SetConfigurationDescription(const std::string&);
/// One-sentence description of the reader's loading "strategy"
std::string GetConfigurationDescription() const;
/// Print configuration description to given stream, for human reader
void PrintConfiguration(std::ostream& os) const;
/// Print output description to given stream, for human reader
void PrintOutputs(std::ostream& os, bool filenameDetails = false) const;
-
+
virtual bool operator==(const DICOMFileReader& other) const = 0;
protected:
DICOMFileReader();
virtual ~DICOMFileReader();
DICOMFileReader(const DICOMFileReader& other);
DICOMFileReader& operator=(const DICOMFileReader& other);
void ClearOutputs();
void SetNumberOfOutputs(unsigned int numberOfOutputs);
void SetOutput(unsigned int index, const DICOMImageBlockDescriptor& output);
/// non-const access to the DICOMImageBlockDescriptor
DICOMImageBlockDescriptor& InternalGetOutput(unsigned int index);
/// Configuration description for human reader, to be implemented by sub-classes
virtual void InternalPrintConfiguration(std::ostream& os) const = 0;
private:
StringList m_InputFilenames;
std::vector< DICOMImageBlockDescriptor > m_Outputs;
std::string m_ConfigLabel;
std::string m_ConfigDescription;
};
}
#endif
diff --git a/Modules/DICOMReader/mitkDICOMFileReaderSelector.cpp b/Modules/DICOMReader/mitkDICOMFileReaderSelector.cpp
index c413cdb01f..d918169a8e 100644
--- a/Modules/DICOMReader/mitkDICOMFileReaderSelector.cpp
+++ b/Modules/DICOMReader/mitkDICOMFileReaderSelector.cpp
@@ -1,244 +1,260 @@
/*===================================================================
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 "mitkDICOMFileReaderSelector.h"
#include "mitkDICOMReaderConfigurator.h"
-#include "mitkModuleContext.h"
+#include "mitkDICOMGDCMTagScanner.h"
+#include
#include
#include
#include
#include
mitk::DICOMFileReaderSelector
::DICOMFileReaderSelector()
{
}
mitk::DICOMFileReaderSelector
::~DICOMFileReaderSelector()
{
}
std::list
mitk::DICOMFileReaderSelector
::GetAllConfiguredReaders() const
{
return m_Readers;
}
void
mitk::DICOMFileReaderSelector
::AddConfigsFromResources(const std::string& path)
{
std::vector configs = GetModuleContext()->GetModule()->FindResources(path, "*.xml", false);
for (std::vector::iterator iter = configs.begin();
iter != configs.end();
++iter)
{
ModuleResource& resource = *iter;
if (resource.IsValid())
{
ModuleResourceStream stream(resource);
// read all into string s
std::string s;
stream.seekg(0, std::ios::end);
s.reserve(stream.tellg());
stream.seekg(0, std::ios::beg);
s.assign((std::istreambuf_iterator(stream)),
std::istreambuf_iterator());
this->AddConfig(s);
}
}
}
void
mitk::DICOMFileReaderSelector
::AddConfigFromResource(ModuleResource& resource)
{
if (resource.IsValid())
{
ModuleResourceStream stream(resource);
// read all into string s
std::string s;
stream.seekg(0, std::ios::end);
s.reserve(stream.tellg());
stream.seekg(0, std::ios::beg);
s.assign((std::istreambuf_iterator(stream)),
std::istreambuf_iterator());
this->AddConfig(s);
}
}
void
mitk::DICOMFileReaderSelector
::AddConfigFromResource(const std::string& resourcename)
{
ModuleResource r = GetModuleContext()->GetModule()->GetResource(resourcename);
this->AddConfigFromResource(r);
}
void
mitk::DICOMFileReaderSelector
::AddFileReaderCanditate(DICOMFileReader::Pointer reader)
{
if (reader.IsNotNull())
{
m_Readers.push_back( reader );
}
}
void
mitk::DICOMFileReaderSelector
::LoadBuiltIn3DConfigs()
{
//this->AddConfigsFromResources("configurations/3D");
// in this order of preference...
this->AddConfigFromResource("configurations/3D/instancenumber.xml");
this->AddConfigFromResource("configurations/3D/slicelocation.xml");
this->AddConfigFromResource("configurations/3D/imageposition.xml");
this->AddConfigFromResource("configurations/3D/imageposition_byacquisition.xml");
//this->AddConfigFromResource("configurations/3D/imagetime.xml"); // no sense in this one? want to see a real-world example first
this->AddConfigFromResource("configurations/3D/classicreader.xml"); // currently is 3D+t actually
}
void
mitk::DICOMFileReaderSelector
::LoadBuiltIn3DnTConfigs()
{
this->AddConfigsFromResources("configurations/3DnT");
}
void
mitk::DICOMFileReaderSelector
::AddConfig(const std::string& xmlDescription)
{
DICOMReaderConfigurator::Pointer configurator = DICOMReaderConfigurator::New();
DICOMFileReader::Pointer reader = configurator->CreateFromUTF8ConfigString(xmlDescription);
if (reader.IsNotNull())
{
m_Readers.push_back( reader );
m_PossibleConfigurations.push_back(xmlDescription);
}
else
{
std::stringstream ss;
ss << "Could not parse reader configuration. Ignoring it.";
throw std::invalid_argument( ss.str() );
}
}
void
mitk::DICOMFileReaderSelector
::AddConfigFile(const std::string& filename)
{
std::ifstream file(filename.c_str());
std::string s;
file.seekg(0, std::ios::end);
s.reserve(file.tellg());
file.seekg(0, std::ios::beg);
s.assign((std::istreambuf_iterator(file)),
std::istreambuf_iterator());
this->AddConfig(s);
}
void
mitk::DICOMFileReaderSelector
::SetInputFiles(StringList filenames)
{
m_InputFilenames = filenames;
}
const mitk::StringList&
mitk::DICOMFileReaderSelector
::GetInputFiles() const
{
return m_InputFilenames;
}
mitk::DICOMFileReader::Pointer
mitk::DICOMFileReaderSelector
::GetFirstReaderWithMinimumNumberOfOutputImages()
{
ReaderList workingCandidates;
+ // do the tag scanning externally and just ONCE
+ DICOMGDCMTagScanner::Pointer gdcmScanner = DICOMGDCMTagScanner::New();
+ gdcmScanner->SetInputFiles( m_InputFilenames );
+
+ // let all readers analyze the file set
+ for (ReaderList::iterator rIter = m_Readers.begin();
+ rIter != m_Readers.end();
+ ++rIter)
+ {
+ gdcmScanner->AddTags( (*rIter)->GetTagsOfInterest() );
+ }
+
+ gdcmScanner->Scan();
+
// let all readers analyze the file set
unsigned int readerIndex(0);
for (ReaderList::iterator rIter = m_Readers.begin();
rIter != m_Readers.end();
++readerIndex, ++rIter)
{
(*rIter)->SetInputFiles( m_InputFilenames );
+ (*rIter)->SetTagCache( gdcmScanner.GetPointer() );
try
{
(*rIter)->AnalyzeInputFiles();
workingCandidates.push_back( *rIter );
MITK_INFO << "Reader " << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << ") suggests " << (*rIter)->GetNumberOfOutputs() << " 3D blocks";
if ((*rIter)->GetNumberOfOutputs() == 1)
{
MITK_DEBUG << "Early out with reader #" << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << "), less than 1 block is not possible";
return *rIter;
}
}
catch (std::exception& e)
{
MITK_ERROR << "Reader " << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << ") threw exception during file analysis, ignoring this reader. Exception: " << e.what();
}
catch (...)
{
MITK_ERROR << "Reader " << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << ") threw unknown exception during file analysis, ignoring this reader.";
}
}
DICOMFileReader::Pointer bestReader;
unsigned int minimumNumberOfOutputs = std::numeric_limits::max();
readerIndex = 0;
unsigned int bestReaderIndex(0);
// select the reader with the minimum number of mitk::Images as output
for (ReaderList::iterator rIter = workingCandidates.begin();
rIter != workingCandidates.end();
++readerIndex, ++rIter)
{
unsigned int thisReadersNumberOfOutputs = (*rIter)->GetNumberOfOutputs();
if ( thisReadersNumberOfOutputs > 0 // we don't count readers that don't actually produce output
&& thisReadersNumberOfOutputs < minimumNumberOfOutputs )
{
minimumNumberOfOutputs = (*rIter)->GetNumberOfOutputs();
bestReader = *rIter;
bestReaderIndex = readerIndex;
}
}
MITK_DEBUG << "Decided for reader #" << bestReaderIndex << " (" << bestReader->GetConfigurationLabel() << ")";
MITK_DEBUG << m_PossibleConfigurations[bestReaderIndex];
return bestReader;
}
diff --git a/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp b/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp
index a2f0941a25..5b68abfb10 100644
--- a/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp
+++ b/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.cpp
@@ -1,690 +1,701 @@
/*===================================================================
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.
===================================================================*/
//#define MBILOG_ENABLE_DEBUG
#include "mitkDICOMITKSeriesGDCMReader.h"
#include "mitkITKDICOMSeriesReaderHelper.h"
#include "mitkGantryTiltInformation.h"
#include "mitkDICOMTagBasedSorter.h"
#include "mitkDICOMGDCMTagScanner.h"
#include
#include
mitk::DICOMITKSeriesGDCMReader
::DICOMITKSeriesGDCMReader(unsigned int decimalPlacesForOrientation)
:DICOMFileReader()
,m_FixTiltByShearing(true)
,m_DecimalPlacesForOrientation(decimalPlacesForOrientation)
{
this->EnsureMandatorySortersArePresent(decimalPlacesForOrientation);
}
mitk::DICOMITKSeriesGDCMReader
::DICOMITKSeriesGDCMReader(const DICOMITKSeriesGDCMReader& other )
:itk::Object()
,DICOMFileReader(other)
,m_FixTiltByShearing(false)
,m_Sorter( other.m_Sorter ) // TODO should clone the list items
,m_EquiDistantBlocksSorter( other.m_EquiDistantBlocksSorter->Clone() )
,m_NormalDirectionConsistencySorter( other.m_NormalDirectionConsistencySorter->Clone() )
,m_DecimalPlacesForOrientation(other.m_DecimalPlacesForOrientation)
{
}
mitk::DICOMITKSeriesGDCMReader
::~DICOMITKSeriesGDCMReader()
{
}
mitk::DICOMITKSeriesGDCMReader&
mitk::DICOMITKSeriesGDCMReader
::operator=(const DICOMITKSeriesGDCMReader& other)
{
if (this != &other)
{
DICOMFileReader::operator=(other);
this->m_FixTiltByShearing = other.m_FixTiltByShearing;
this->m_Sorter = other.m_Sorter; // TODO should clone the list items
this->m_EquiDistantBlocksSorter = other.m_EquiDistantBlocksSorter->Clone();
this->m_NormalDirectionConsistencySorter = other.m_NormalDirectionConsistencySorter->Clone();
this->m_DecimalPlacesForOrientation = other.m_DecimalPlacesForOrientation;
}
return *this;
}
bool
mitk::DICOMITKSeriesGDCMReader
::operator==(const DICOMFileReader& other) const
{
if (const Self* otherSelf = dynamic_cast(&other))
{
if ( this->m_FixTiltByShearing == otherSelf->m_FixTiltByShearing
&& *(this->m_EquiDistantBlocksSorter) == *(otherSelf->m_EquiDistantBlocksSorter)
&& (fabs(this->m_DecimalPlacesForOrientation - otherSelf->m_DecimalPlacesForOrientation) < eps)
)
{
// test sorters for equality
if (this->m_Sorter.size() != otherSelf->m_Sorter.size()) return false;
SorterList::const_iterator mySorterIter = this->m_Sorter.begin();
SorterList::const_iterator oSorterIter = otherSelf->m_Sorter.begin();
for(; mySorterIter != this->m_Sorter.end() && oSorterIter != otherSelf->m_Sorter.end();
++mySorterIter, ++oSorterIter)
{
if ( ! (**mySorterIter == **oSorterIter ) ) return false; // this sorter differs
}
// nothing differs ==> all is equal
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
void
mitk::DICOMITKSeriesGDCMReader
::SetFixTiltByShearing(bool on)
{
m_FixTiltByShearing = on;
}
bool
mitk::DICOMITKSeriesGDCMReader
::GetFixTiltByShearing() const
{
return m_FixTiltByShearing;
}
void
mitk::DICOMITKSeriesGDCMReader
::SetAcceptTwoSlicesGroups(bool accept)
{
m_EquiDistantBlocksSorter->SetAcceptTwoSlicesGroups(accept);
}
bool
mitk::DICOMITKSeriesGDCMReader
::GetAcceptTwoSlicesGroups() const
{
return m_EquiDistantBlocksSorter->GetAcceptTwoSlicesGroups();
}
mitk::DICOMGDCMImageFrameList
mitk::DICOMITKSeriesGDCMReader
::FromDICOMDatasetList(const DICOMDatasetList& input)
{
DICOMGDCMImageFrameList output;
output.reserve(input.size());
for(DICOMDatasetList::const_iterator inputIter = input.begin();
inputIter != input.end();
++inputIter)
{
DICOMGDCMImageFrameInfo* gfi = dynamic_cast(*inputIter);
assert(gfi);
output.push_back(gfi);
}
return output;
}
mitk::DICOMDatasetList
mitk::DICOMITKSeriesGDCMReader
::ToDICOMDatasetList(const DICOMGDCMImageFrameList& input)
{
DICOMDatasetList output;
output.reserve(input.size());
for(DICOMGDCMImageFrameList::const_iterator inputIter = input.begin();
inputIter != input.end();
++inputIter)
{
DICOMDatasetAccess* da = inputIter->GetPointer();
assert(da);
output.push_back(da);
}
return output;
}
mitk::DICOMImageFrameList
mitk::DICOMITKSeriesGDCMReader
::ToDICOMImageFrameList(const DICOMGDCMImageFrameList& input)
{
DICOMImageFrameList output;
output.reserve(input.size());
for(DICOMGDCMImageFrameList::const_iterator inputIter = input.begin();
inputIter != input.end();
++inputIter)
{
DICOMImageFrameInfo::Pointer fi = (*inputIter)->GetFrameInfo();
assert(fi.IsNotNull());
output.push_back(fi);
}
return output;
}
void
mitk::DICOMITKSeriesGDCMReader
::InternalPrintConfiguration(std::ostream& os) const
{
unsigned int sortIndex(1);
for(SorterList::const_iterator sorterIter = m_Sorter.begin();
sorterIter != m_Sorter.end();
++sortIndex, ++sorterIter)
{
os << "Sorting step " << sortIndex << ":" << std::endl;
(*sorterIter)->PrintConfiguration(os, " ");
}
os << "Sorting step " << sortIndex << ":" << std::endl;
m_EquiDistantBlocksSorter->PrintConfiguration(os, " ");
}
std::string
mitk::DICOMITKSeriesGDCMReader
::GetActiveLocale() const
{
return setlocale(LC_NUMERIC, NULL);
}
void
mitk::DICOMITKSeriesGDCMReader
::PushLocale() const
{
std::string currentCLocale = setlocale(LC_NUMERIC, NULL);
m_ReplacedCLocales.push( currentCLocale );
setlocale(LC_NUMERIC, "C");
std::locale currentCinLocale( std::cin.getloc() );
m_ReplacedCinLocales.push( currentCinLocale );
std::locale l( "C" );
std::cin.imbue(l);
}
void
mitk::DICOMITKSeriesGDCMReader
::PopLocale() const
{
if (!m_ReplacedCLocales.empty())
{
setlocale(LC_NUMERIC, m_ReplacedCLocales.top().c_str());
m_ReplacedCLocales.pop();
}
else
{
MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader.";
}
if (!m_ReplacedCinLocales.empty())
{
std::cin.imbue( m_ReplacedCinLocales.top() );
m_ReplacedCinLocales.pop();
}
else
{
MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader.";
}
}
mitk::DICOMITKSeriesGDCMReader::SortingBlockList
mitk::DICOMITKSeriesGDCMReader
::Condense3DBlocks(SortingBlockList& input)
{
return input; // to be implemented differently by sub-classes
}
void
mitk::DICOMITKSeriesGDCMReader
::AnalyzeInputFiles()
{
itk::TimeProbesCollectorBase timer;
timer.Start("Reset");
this->ClearOutputs();
- DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New();
- m_TagCache = filescanner.GetPointer(); // keep alive and make accessible to sub-classes
timer.Stop("Reset");
// prepare initial sorting (== list of input files)
StringList inputFilenames = this->GetInputFiles();
- filescanner->SetInputFiles(inputFilenames);
-
timer.Start("Check appropriateness of input files");
if ( inputFilenames.empty()
||
!this->CanHandleFile( inputFilenames.front() ) // first
||
!this->CanHandleFile( inputFilenames.back() ) // last
||
!this->CanHandleFile( inputFilenames[ inputFilenames.size() / 2] ) // roughly central file
)
{
// TODO a read-as-many-as-possible fallback could be implemented here
MITK_DEBUG << "Reader unable to process files..";
return;
}
timer.Stop("Check appropriateness of input files");
// scan files for sorting-relevant tags
- timer.Start("Setup scanning");
- filescanner->AddTags( this->GetTagsOfInterest() );
+ if (m_TagCache.IsNull())
+ {
+ timer.Start("Tag scanning");
+ DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New();
+ m_TagCache = filescanner.GetPointer(); // keep alive and make accessible to sub-classes
- timer.Stop("Setup scanning");
+ filescanner->SetInputFiles(inputFilenames);
+ filescanner->AddTags( this->GetTagsOfInterest() );
- timer.Start("Tag scanning");
- PushLocale();
- filescanner->Scan();
- PopLocale();
- timer.Stop("Tag scanning");
+ PushLocale();
+ filescanner->Scan();
+ PopLocale();
+
+ timer.Stop("Tag scanning");
+ }
+ else
+ {
+ // ensure that the tag cache contains our required tags AND files and has scanned!
+ }
- timer.Start("Setup sorting");
m_SortingResultInProgress.clear();
- m_SortingResultInProgress.push_back(filescanner->GetFrameInfoList());
- timer.Stop("Setup sorting");
+ // TODO this should look better!
+ m_SortingResultInProgress.push_back( static_cast(m_TagCache.GetPointer())->GetFrameInfoList() );
// sort and split blocks as configured
timer.Start("Sorting frames");
unsigned int sorterIndex = 0;
for(SorterList::iterator sorterIter = m_Sorter.begin();
sorterIter != m_Sorter.end();
++sorterIndex, ++sorterIter)
{
m_SortingResultInProgress = this->InternalExecuteSortingStep(sorterIndex, *sorterIter, m_SortingResultInProgress, &timer);
}
// a last extra-sorting step: ensure equidistant slices
m_SortingResultInProgress = this->InternalExecuteSortingStep(sorterIndex++, m_EquiDistantBlocksSorter.GetPointer(), m_SortingResultInProgress, &timer);
timer.Stop("Sorting frames");
timer.Start("Condensing 3D blocks (3D+t or vector values)");
m_SortingResultInProgress = this->Condense3DBlocks( m_SortingResultInProgress );
timer.Stop("Condensing 3D blocks (3D+t or vector values)");
// provide final result as output
timer.Start("Output");
unsigned int o = this->GetNumberOfOutputs();
this->SetNumberOfOutputs( o + m_SortingResultInProgress.size() ); // Condense3DBlocks may already have added outputs!
for (SortingBlockList::iterator blockIter = m_SortingResultInProgress.begin();
blockIter != m_SortingResultInProgress.end();
++o, ++blockIter)
{
DICOMGDCMImageFrameList& gdcmFrameInfoList = *blockIter;
assert(!gdcmFrameInfoList.empty());
// reverse frames if necessary
// update tilt information from absolute last sorting
DICOMDatasetList datasetList = ToDICOMDatasetList( gdcmFrameInfoList );
m_NormalDirectionConsistencySorter->SetInput( datasetList );
m_NormalDirectionConsistencySorter->Sort();
DICOMGDCMImageFrameList sortedGdcmInfoFrameList = FromDICOMDatasetList( m_NormalDirectionConsistencySorter->GetOutput(0) );
const GantryTiltInformation& tiltInfo = m_NormalDirectionConsistencySorter->GetTiltInformation();
// set frame list for current block
DICOMImageFrameList frameList = ToDICOMImageFrameList( sortedGdcmInfoFrameList );
assert(!frameList.empty());
DICOMImageBlockDescriptor block;
- block.SetTagCache( filescanner ); // important: this must be before SetImageFrameList(), because SetImageFrameList will trigger reading of lots of interesting tags!
+ block.SetTagCache( this->GetTagCache() ); // important: this must be before SetImageFrameList(), because SetImageFrameList will trigger reading of lots of interesting tags!
block.SetImageFrameList( frameList );
block.SetTiltInformation( tiltInfo );
block.SetReaderImplementationLevel( this->GetReaderImplementationLevel( block.GetSOPClassUID() ) );
this->SetOutput( o, block );
}
timer.Stop("Output");
#ifdef MBILOG_ENABLE_DEBUG
std::cout << "---------------------------------------------------------------" << std::endl;
timer.Report( std::cout );
std::cout << "---------------------------------------------------------------" << std::endl;
#endif
}
mitk::DICOMITKSeriesGDCMReader::SortingBlockList
mitk::DICOMITKSeriesGDCMReader
::InternalExecuteSortingStep(
unsigned int sortingStepIndex,
DICOMDatasetSorter::Pointer sorter,
const SortingBlockList& input,
itk::TimeProbesCollectorBase* timer)
{
SortingBlockList nextStepSorting; // we should not modify our input list while processing it
std::stringstream ss; ss << "Sorting step " << sortingStepIndex << " '";
sorter->PrintConfiguration(ss);
ss << "'";
timer->Start( ss.str().c_str() );
nextStepSorting.clear();
MITK_DEBUG << "================================================================================";
MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ": " << input.size() << " groups input";
unsigned int groupIndex = 0;
for(SortingBlockList::const_iterator blockIter = input.begin();
blockIter != input.end();
++groupIndex, ++blockIter)
{
const DICOMGDCMImageFrameList& gdcmInfoFrameList = *blockIter;
DICOMDatasetList datasetList = ToDICOMDatasetList( gdcmInfoFrameList );
MITK_DEBUG << "--------------------------------------------------------------------------------";
MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ", dataset group " << groupIndex << " (" << datasetList.size() << " datasets): ";
for (DICOMDatasetList::iterator oi = datasetList.begin();
oi != datasetList.end();
++oi)
{
MITK_DEBUG << " INPUT : " << (*oi)->GetFilenameIfAvailable();
}
sorter->SetInput(datasetList);
sorter->Sort();
unsigned int numberOfResultingBlocks = sorter->GetNumberOfOutputs();
for (unsigned int b = 0; b < numberOfResultingBlocks; ++b)
{
DICOMDatasetList blockResult = sorter->GetOutput(b);
for (DICOMDatasetList::iterator oi = blockResult.begin();
oi != blockResult.end();
++oi)
{
MITK_DEBUG << " OUTPUT(" << b << ") :" << (*oi)->GetFilenameIfAvailable();
}
DICOMGDCMImageFrameList sortedGdcmInfoFrameList = FromDICOMDatasetList(blockResult);
nextStepSorting.push_back( sortedGdcmInfoFrameList );
}
}
timer->Stop( ss.str().c_str() );
return nextStepSorting;
}
mitk::ReaderImplementationLevel
mitk::DICOMITKSeriesGDCMReader
::GetReaderImplementationLevel(const std::string sopClassUID) const
{
if (sopClassUID.empty())
{
return SOPClassUnknown;
}
gdcm::UIDs uidKnowledge;
uidKnowledge.SetFromUID( sopClassUID.c_str() );
gdcm::UIDs::TSType gdcmType = uidKnowledge;
switch (gdcmType)
{
case gdcm::UIDs::CTImageStorage:
case gdcm::UIDs::MRImageStorage:
case gdcm::UIDs::PositronEmissionTomographyImageStorage:
case gdcm::UIDs::ComputedRadiographyImageStorage:
case gdcm::UIDs::DigitalXRayImageStorageForPresentation:
case gdcm::UIDs::DigitalXRayImageStorageForProcessing:
return SOPClassSupported;
case gdcm::UIDs::NuclearMedicineImageStorage:
return SOPClassPartlySupported;
case gdcm::UIDs::SecondaryCaptureImageStorage:
return SOPClassImplemented;
default:
return SOPClassUnsupported;
}
}
// void AllocateOutputImages();
bool
mitk::DICOMITKSeriesGDCMReader
::LoadImages()
{
bool success = true;
unsigned int numberOfOutputs = this->GetNumberOfOutputs();
for (unsigned int o = 0; o < numberOfOutputs; ++o)
{
success &= this->LoadMitkImageForOutput(o);
}
return success;
}
bool
mitk::DICOMITKSeriesGDCMReader
::LoadMitkImageForImageBlockDescriptor(DICOMImageBlockDescriptor& block) const
{
PushLocale();
const DICOMImageFrameList& frames = block.GetImageFrameList();
const GantryTiltInformation tiltInfo = block.GetTiltInformation();
bool hasTilt = tiltInfo.IsRegularGantryTilt();
ITKDICOMSeriesReaderHelper::StringContainer filenames;
for (DICOMImageFrameList::const_iterator frameIter = frames.begin();
frameIter != frames.end();
++frameIter)
{
filenames.push_back( (*frameIter)->Filename );
}
mitk::ITKDICOMSeriesReaderHelper helper;
bool success(true);
try
{
mitk::Image::Pointer mitkImage = helper.Load( filenames, m_FixTiltByShearing && hasTilt, tiltInfo );
block.SetMitkImage( mitkImage );
}
catch (std::exception& e)
{
success = false;
MITK_ERROR << "Exception during image loading: " << e.what();
}
PopLocale();
return success;
}
bool
mitk::DICOMITKSeriesGDCMReader
::LoadMitkImageForOutput(unsigned int o)
{
DICOMImageBlockDescriptor& block = this->InternalGetOutput(o);
return this->LoadMitkImageForImageBlockDescriptor(block);
}
bool
mitk::DICOMITKSeriesGDCMReader
::CanHandleFile(const std::string& filename)
{
return ITKDICOMSeriesReaderHelper::CanHandleFile(filename);
}
void
mitk::DICOMITKSeriesGDCMReader
::AddSortingElement(DICOMDatasetSorter* sorter, bool atFront)
{
assert(sorter);
if (atFront)
{
m_Sorter.push_front( sorter );
}
else
{
m_Sorter.push_back( sorter );
}
}
mitk::DICOMITKSeriesGDCMReader::ConstSorterList
mitk::DICOMITKSeriesGDCMReader
::GetFreelyConfiguredSortingElements() const
{
std::list result;
unsigned int sortIndex(0);
for(SorterList::const_iterator sorterIter = m_Sorter.begin();
sorterIter != m_Sorter.end();
++sortIndex, ++sorterIter)
{
if (sortIndex > 0) // ignore first element (see EnsureMandatorySortersArePresent)
{
result.push_back( (*sorterIter).GetPointer() );
}
}
return result;
}
void
mitk::DICOMITKSeriesGDCMReader
::EnsureMandatorySortersArePresent(unsigned int decimalPlacesForOrientation)
{
DICOMTagBasedSorter::Pointer splitter = DICOMTagBasedSorter::New();
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0010) ); // Number of Rows
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0011) ); // Number of Columns
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0030) ); // Pixel Spacing
splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x1164) ); // Imager Pixel Spacing
splitter->AddDistinguishingTag( DICOMTag(0x0020, 0x0037), new mitk::DICOMTagBasedSorter::CutDecimalPlaces(decimalPlacesForOrientation) ); // Image Orientation (Patient)
splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x0050) ); // Slice Thickness
splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0008) ); // Number of Frames
this->AddSortingElement( splitter, true ); // true = at front
if (m_EquiDistantBlocksSorter.IsNull())
{
m_EquiDistantBlocksSorter = mitk::EquiDistantBlocksSorter::New();
}
m_EquiDistantBlocksSorter->SetAcceptTilt( m_FixTiltByShearing );
if (m_NormalDirectionConsistencySorter.IsNull())
{
m_NormalDirectionConsistencySorter = mitk::NormalDirectionConsistencySorter::New();
}
}
void
mitk::DICOMITKSeriesGDCMReader
::SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistance)
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
m_EquiDistantBlocksSorter->SetToleratedOriginOffsetToAdaptive(fractionOfInterSliceDistance);
}
void
mitk::DICOMITKSeriesGDCMReader
::SetToleratedOriginOffset(double millimeters)
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
m_EquiDistantBlocksSorter->SetToleratedOriginOffset(millimeters);
}
double
mitk::DICOMITKSeriesGDCMReader
::GetToleratedOriginError() const
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
return m_EquiDistantBlocksSorter->GetToleratedOriginOffset();
}
bool
mitk::DICOMITKSeriesGDCMReader
::IsToleratedOriginOffsetAbsolute() const
{
assert( m_EquiDistantBlocksSorter.IsNotNull() );
return m_EquiDistantBlocksSorter->IsToleratedOriginOffsetAbsolute();
}
double
mitk::DICOMITKSeriesGDCMReader
::GetDecimalPlacesForOrientation() const
{
return m_DecimalPlacesForOrientation;
}
mitk::DICOMTagCache::Pointer
mitk::DICOMITKSeriesGDCMReader
::GetTagCache() const
{
return m_TagCache;
}
+void
+mitk::DICOMITKSeriesGDCMReader
+::SetTagCache(DICOMTagCache::Pointer tagCache)
+{
+ m_TagCache = tagCache;
+}
+
mitk::DICOMTagList
mitk::DICOMITKSeriesGDCMReader
::GetTagsOfInterest() const
{
DICOMTagList completeList;
for(SorterList::const_iterator sorterIter = m_Sorter.begin();
sorterIter != m_Sorter.end();
++sorterIter)
{
assert(sorterIter->IsNotNull());
DICOMTagList tags = (*sorterIter)->GetTagsOfInterest();
completeList.insert( completeList.end(), tags.begin(), tags.end() );
}
// Add some of our own interest
// TODO all tags that are needed in DICOMImageBlockDescriptor should be added by DICOMFileReader (this code location here should query all superclasses for tags)
completeList.push_back( DICOMTag(0x0018,0x1164) ); // pixel spacing
completeList.push_back( DICOMTag(0x0028,0x0030) ); // imager pixel spacing
completeList.push_back( DICOMTag(0x0028,0x1050) ); // window center
completeList.push_back( DICOMTag(0x0028,0x1051) ); // window width
completeList.push_back( DICOMTag(0x0008,0x0008) ); // image type
completeList.push_back( DICOMTag(0x0028,0x0004) ); // photometric interpretation
completeList.push_back( DICOMTag(0x0020,0x1041) ); // slice location
completeList.push_back( DICOMTag(0x0020,0x0013) ); // instance number
completeList.push_back( DICOMTag(0x0008,0x0016) ); // sop class UID
completeList.push_back( DICOMTag(0x0008,0x0018) ); // sop instance UID
completeList.push_back( DICOMTag(0x0020,0x0011) ); // series number
completeList.push_back( DICOMTag(0x0008,0x1030) ); // study description
completeList.push_back( DICOMTag(0x0008,0x103e) ); // series description
completeList.push_back( DICOMTag(0x0008,0x0060) ); // modality
completeList.push_back( DICOMTag(0x0020,0x0012) ); // acquisition number
completeList.push_back( DICOMTag(0x0018,0x0024) ); // sequence name
completeList.push_back( DICOMTag(0x0020,0x0037) ); // image orientation
completeList.push_back( DICOMTag(0x0020,0x0032) ); // ipp
return completeList;
}
diff --git a/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.h b/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.h
index bd0064aafd..e116abdee9 100644
--- a/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.h
+++ b/Modules/DICOMReader/mitkDICOMITKSeriesGDCMReader.h
@@ -1,356 +1,357 @@
/*===================================================================
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 mitkDICOMITKSeriesGDCMReader_h
#define mitkDICOMITKSeriesGDCMReader_h
#include "mitkDICOMFileReader.h"
#include "mitkDICOMDatasetSorter.h"
#include "mitkDICOMGDCMImageFrameInfo.h"
#include "mitkEquiDistantBlocksSorter.h"
#include "mitkNormalDirectionConsistencySorter.h"
#include "mitkITKDICOMSeriesReaderHelper.h"
#include "DICOMReaderExports.h"
#include
namespace itk
{
class TimeProbesCollectorBase;
}
namespace mitk
{
/**
\ingroup DICOMReaderModule
\brief Flexible reader based on itk::ImageSeriesReader and GDCM, for single-slice modalities like CT, MR, PET, CR, etc.
Implements the loading processed as structured by DICOMFileReader offers configuration
of its loading strategy.
Documentation sections:
- \ref DICOMITKSeriesGDCMReader_LoadingStrategy
- \ref DICOMITKSeriesGDCMReader_ForcedConfiguration
- \ref DICOMITKSeriesGDCMReader_UserConfiguration
- \ref DICOMITKSeriesGDCMReader_GantryTilt
- \ref DICOMITKSeriesGDCMReader_Testing
- \ref DICOMITKSeriesGDCMReader_Internals
- \ref DICOMITKSeriesGDCMReader_RelatedClasses
- \ref DICOMITKSeriesGDCMReader_TiltInternals
- \ref DICOMITKSeriesGDCMReader_Condensing
\section DICOMITKSeriesGDCMReader_LoadingStrategy Loading strategy
The set of input files is processed by a number of DICOMDatasetSorter objects which may do two sort of things:
1. split a list of input frames into multiple lists, based on DICOM tags such as "Rows", "Columns", which cannot be mixed within a single mitk::Image
2. sort the frames within the input lists, based on the values of DICOM tags such as "Image Position Patient"
When the DICOMITKSeriesGDCMReader is configured with DICOMDatasetSorter%s, the list of input files is processed
as follows:
1. build an initial set of output groups, simply by grouping all input files.
2. for each configured DICOMDatasetSorter, process:
- for each output group:
1. set this group's files as input to the sorter
2. let the sorter sort (and split)
3. integrate the sorter's output groups with our own output groups
\section DICOMITKSeriesGDCMReader_ForcedConfiguration Forced Configuration
In all cases, the reader will add two DICOMDatasetSorter objects that are required to load
mitk::Images properly via itk::ImageSeriesReader:
1. As a \b first step, the input files will be split into groups that are not compatible because they differ in essential aspects:
- (0028,0010) Number of Rows
- (0028,0011) Number of Columns
- (0028,0030) Pixel Spacing
- (0018,1164) Imager Pixel Spacing
- (0020,0037) %Image Orientation (Patient)
- (0018,0050) Slice Thickness
- (0028,0008) Number of Frames
2. As are two forced \b last steps:
1. There will always be an instance of EquiDistantBlocksSorter,
which ensures that there is an equal distance between all the frames of an Image.
This is required to achieve correct geometrical positions in the mitk::Image,
i.e. it is essential to be able to make measurements in images.
- whether or not the distance is required to be orthogonal to the image planes is configured by SetFixTiltByShearing().
- during this check, we need to tolerate some minor errors in documented vs. calculated image origins.
The amount of tolerance can be adjusted by SetToleratedOriginOffset() and SetToleratedOriginOffsetToAdaptive().
Please see EquiDistantBlocksSorter for more details. The default should be good for most cases.
2. There is always an instance of NormalDirectionConsistencySorter,
which makes the order of images go along the image normals (see NormalDirectionConsistencySorter)
\section DICOMITKSeriesGDCMReader_UserConfiguration User Configuration
The user of this class can add more sorting steps (similar to the one described in above section) by calling AddSortingElement().
Usually, an application will add sorting by "Image Position Patient", by "Instance Number", and by other relevant tags here.
\section DICOMITKSeriesGDCMReader_GantryTilt Gantry tilt handling
When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align
anymore. This scanner feature is used for example to reduce metal artifacs (e.g. Lee C , Evaluation of Using CT
Gantry Tilt Scan on Head and Neck Cancer Patients with Dental Structure: Scans Show Less Metal Artifacts. Presented
at: Radiological Society of North America 2011 Scientific Assembly and Annual Meeting; November 27- December 2,
2011 Chicago IL.).
The acquired planes of such CT series do not match the expectations of a orthogonal geometry in mitk::Image: if you
stack the slices, they show a small shift along the Y axis:
\verbatim
without tilt with tilt
|||||| //////
|||||| //////
-- |||||| --------- ////// -------- table orientation
|||||| //////
|||||| //////
Stacked slices:
without tilt with tilt
-------------- --------------
-------------- --------------
-------------- --------------
-------------- --------------
-------------- --------------
\endverbatim
As such gemetries do not "work" in conjunction with mitk::Image, DICOMITKSeriesGDCMReader is able to perform a correction for such series.
Whether or not such correction should be attempted is controlled by SetFixTiltByShearing(), the default being correction.
For details, see "Internals" below.
\section DICOMITKSeriesGDCMReader_Testing Testing
A number of tests is implemented in module DICOMTesting, which is documented at \ref DICOMTesting.
\section DICOMITKSeriesGDCMReader_Internals Class internals
Internally, the class is based on GDCM and it depends heavily on the gdcm::Scanner class.
Since the sorting elements (see DICOMDatasetSorter and DICOMSortCriterion) can access tags only via the DICOMDatasetAccess interface,
BUT DICOMITKSeriesGDCMReader holds a list of more specific classes DICOMGDCMImageFrameInfo, we must convert between the two
types sometimes. This explains the methods ToDICOMDatasetList(), FromDICOMDatasetList().
The intermediate result of all the sorting efforts is held in m_SortingResultInProgress,
which is modified through InternalExecuteSortingStep().
\subsection DICOMITKSeriesGDCMReader_RelatedClasses Overview of related classes
The following diagram gives an overview of the related classes:
\image html implementeditkseriesgdcmreader.jpg
\subsection DICOMITKSeriesGDCMReader_TiltInternals Details about the tilt correction
The gantry tilt "correction" algorithm fixes two errors introduced by ITK's ImageSeriesReader:
- the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using itk::ResampleFilter.
- the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the slice distance in this special case)
Both errors are introduced in
itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20)
For the correction, we examine two consecutive slices of a series, both described as a pair (origin/orientation):
- we calculate if the first origin is on a line along the normal of the second slice
- if this is not the case, the geometry will not fit a normal mitk::Image/mitk::Geometry3D
- we then project the second origin into the first slice's coordinate system to quantify the shift
- both is done in class GantryTiltInformation with quite some comments.
The geometry of image stacks with tilted geometries is illustrated below:
- green: the DICOM images as described by their tags: origin as a point with the line indicating the orientation
- red: the output of ITK ImageSeriesReader: wrong, larger spacing, no tilt
- blue: how much a shear must correct
\image html tilt-correction.jpg
\subsection DICOMITKSeriesGDCMReader_Condensing Sub-classes can condense multiple blocks into a single larger block
The sorting/splitting process described above is helpful for at least two more DICOM readers, which either try to load 3D+t images or which load diffusion data.
In both cases, a single pixel of the mitk::Image is made up of multiple values, in one case values over time, in the other case multiple measurements of a single point.
The specialized readers for these cases (e.g. ThreeDnTDICOMSeriesReader) can reuse most of the methods in DICOMITKSeriesGDCMReader,
except that they need an extra step after the usual sorting, in which they can merge already grouped 3D blocks. What blocks are merged
depends on the specialized reader's understanding of these images. To allow for such merging, a method Condense3DBlocks() is called
as an absolute last step of AnalyzeInputFiles(). Given this, a sub-class could implement only LoadImages() and Condense3DBlocks() instead
repeating most of AnalyzeInputFiles().
*/
class DICOMReader_EXPORT DICOMITKSeriesGDCMReader : public DICOMFileReader
{
public:
mitkClassMacro( DICOMITKSeriesGDCMReader, DICOMFileReader );
mitkCloneMacro( DICOMITKSeriesGDCMReader );
itkNewMacro( DICOMITKSeriesGDCMReader );
mitkNewMacro1Param( DICOMITKSeriesGDCMReader, unsigned int );
/**
\brief Runs the sorting / splitting process described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy.
Method required by DICOMFileReader.
*/
virtual void AnalyzeInputFiles();
// void AllocateOutputImages();
/**
\brief Loads images using itk::ImageSeriesReader, potentially applies shearing to correct gantry tilt.
*/
virtual bool LoadImages();
// re-implemented from super-class
virtual bool CanHandleFile(const std::string& filename);
/**
\brief Add an element to the sorting procedure described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy.
*/
virtual void AddSortingElement(DICOMDatasetSorter* sorter, bool atFront = false);
typedef const std::list ConstSorterList;
ConstSorterList GetFreelyConfiguredSortingElements() const;
/**
\brief Controls whether to "fix" tilted acquisitions by shearing the output (see \ref DICOMITKSeriesGDCMReader_GantryTilt).
*/
void SetFixTiltByShearing(bool on);
bool GetFixTiltByShearing() const;
/**
\brief Controls whether groups of only two images are accepted when ensuring consecutive slices via EquiDistantBlocksSorter.
*/
void SetAcceptTwoSlicesGroups(bool accept);
bool GetAcceptTwoSlicesGroups() const;
/**
\brief See \ref DICOMITKSeriesGDCMReader_ForcedConfiguration.
*/
void SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistanct = 0.3);
/**
\brief See \ref DICOMITKSeriesGDCMReader_ForcedConfiguration.
*/
void SetToleratedOriginOffset(double millimeters = 0.005);
double GetToleratedOriginError() const;
bool IsToleratedOriginOffsetAbsolute() const;
double GetDecimalPlacesForOrientation() const;
virtual bool operator==(const DICOMFileReader& other) const;
virtual DICOMTagList GetTagsOfInterest() const;
protected:
virtual void InternalPrintConfiguration(std::ostream& os) const;
/// \brief Return active C locale
std::string GetActiveLocale() const;
/**
\brief Remember current locale on stack, activate "C" locale.
"C" locale is required for correct parsing of numbers by itk::ImageSeriesReader
*/
void PushLocale() const;
/**
\brief Activate last remembered locale from locale stack
"C" locale is required for correct parsing of numbers by itk::ImageSeriesReader
*/
void PopLocale() const;
DICOMITKSeriesGDCMReader(unsigned int decimalPlacesForOrientation = 5);
virtual ~DICOMITKSeriesGDCMReader();
DICOMITKSeriesGDCMReader(const DICOMITKSeriesGDCMReader& other);
DICOMITKSeriesGDCMReader& operator=(const DICOMITKSeriesGDCMReader& other);
/// \brief See \ref DICOMITKSeriesGDCMReader_Internals
DICOMDatasetList ToDICOMDatasetList(const DICOMGDCMImageFrameList& input);
/// \brief See \ref DICOMITKSeriesGDCMReader_Internals
DICOMGDCMImageFrameList FromDICOMDatasetList(const DICOMDatasetList& input);
/// \brief See \ref DICOMITKSeriesGDCMReader_Internals
DICOMImageFrameList ToDICOMImageFrameList(const DICOMGDCMImageFrameList& input);
typedef std::list SortingBlockList;
/**
\brief "Hook" for sub-classes, see \ref DICOMITKSeriesGDCMReader_Condensing
\return REMAINING blocks
*/
virtual SortingBlockList Condense3DBlocks(SortingBlockList& resultOf3DGrouping);
virtual DICOMTagCache::Pointer GetTagCache() const;
+ void SetTagCache(DICOMTagCache::Pointer);
/// \brief Sorting step as described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy
SortingBlockList InternalExecuteSortingStep(
unsigned int sortingStepIndex,
DICOMDatasetSorter::Pointer sorter,
const SortingBlockList& input,
itk::TimeProbesCollectorBase* timer);
/// \brief Loads the mitk::Image by means of an itk::ImageSeriesReader
virtual bool LoadMitkImageForOutput(unsigned int o);
virtual bool LoadMitkImageForImageBlockDescriptor(DICOMImageBlockDescriptor& block) const;
/**
\brief Shear the loaded mitk::Image to "correct" a spatial error introduced by itk::ImageSeriesReader
See \ref DICOMITKSeriesGDCMReader_GantryTilt for details.
*/
Image::Pointer FixupSpacing(Image* mitkImage, const DICOMImageBlockDescriptor& block) const;
/// \brief Describe this reader's confidence for given SOP class UID
ReaderImplementationLevel GetReaderImplementationLevel(const std::string sopClassUID) const;
private:
/// \brief Creates the required sorting steps described in \ref DICOMITKSeriesGDCMReader_ForcedConfiguration
void EnsureMandatorySortersArePresent(unsigned int decimalPlacesForOrientation);
protected:
// NOT nice, made available to ThreeDnTDICOMSeriesReader due to lack of time
bool m_FixTiltByShearing; // could be removed by ITKDICOMSeriesReader NOT flagging tilt unless requested to fix it!
private:
SortingBlockList m_SortingResultInProgress;
typedef std::list SorterList;
SorterList m_Sorter;
protected:
// NOT nice, made available to ThreeDnTDICOMSeriesReader and ClassicDICOMSeriesReader due to lack of time
mitk::EquiDistantBlocksSorter::Pointer m_EquiDistantBlocksSorter;
mitk::NormalDirectionConsistencySorter::Pointer m_NormalDirectionConsistencySorter;
private:
mutable std::stack m_ReplacedCLocales;
mutable std::stack m_ReplacedCinLocales;
double m_DecimalPlacesForOrientation;
DICOMTagCache::Pointer m_TagCache;
};
}
#endif