diff --git a/Modules/DICOM/src/mitkDICOMGDCMImageFrameInfo.cpp b/Modules/DICOM/src/mitkDICOMGDCMImageFrameInfo.cpp
index 0d1e7bc67e..3eafed8c32 100644
--- a/Modules/DICOM/src/mitkDICOMGDCMImageFrameInfo.cpp
+++ b/Modules/DICOM/src/mitkDICOMGDCMImageFrameInfo.cpp
@@ -1,109 +1,89 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkDICOMGDCMImageFrameInfo.h"
 
 mitk::DICOMGDCMImageFrameInfo
 ::DICOMGDCMImageFrameInfo(const std::string& filename, unsigned int frameNo)
 :DICOMDatasetAccessingImageFrameInfo(filename, frameNo)
 ,m_TagForValue()
 {
 }
 
 mitk::DICOMGDCMImageFrameInfo
 ::DICOMGDCMImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo)
 :DICOMDatasetAccessingImageFrameInfo(frameinfo->Filename, frameinfo->FrameNo)
 ,m_TagForValue()
 {
 }
 
 mitk::DICOMGDCMImageFrameInfo
 ::DICOMGDCMImageFrameInfo(const DICOMImageFrameInfo::Pointer& frameinfo, gdcm::Scanner::TagToValue const& tagToValueMapping)
 :DICOMDatasetAccessingImageFrameInfo(frameinfo->Filename, frameinfo->FrameNo)
 ,m_TagForValue(tagToValueMapping)
 {
 }
 
 mitk::DICOMGDCMImageFrameInfo::
 ~DICOMGDCMImageFrameInfo()
 {
 }
 
 mitk::DICOMDatasetFinding
 mitk::DICOMGDCMImageFrameInfo
 ::GetTagValueAsString(const DICOMTag& tag) const
 {
   const auto mappedValue = m_TagForValue.find( gdcm::Tag(tag.GetGroup(), tag.GetElement()) );
   DICOMDatasetFinding result;
 
   if (mappedValue != m_TagForValue.cend())
   {
     result.isValid = true;
 
     if (mappedValue->second != nullptr)
     {
       std::string s(mappedValue->second);
       try
       {
         result.value = s.erase(s.find_last_not_of(" \n\r\t")+1);
       }
       catch(...)
       {
         result.value = s;
       }
     }
     else
     {
       result.value = "";
     }
   }
-  else
-  {
-    const DICOMTag tagImagePositionPatient = DICOMTag(0x0020,0x0032); // Image Position (Patient)
-    const DICOMTag    tagImageOrientation = DICOMTag(0x0020, 0x0037); // Image Orientation
 
-    if (tag == tagImagePositionPatient)
-    {
-      result.isValid = true;
-      result.value = std::string("0\\0\\0");
-    }
-    else if (tag == tagImageOrientation)
-    {
-      result.isValid = true;
-      result.value = std::string("1\\0\\0\\0\\1\\0");
-    }
-    else
-    {
-      result.isValid = false;
-      result.value = "";
-    }
-  }
   return result;
 }
 
 mitk::DICOMDatasetAccess::FindingsListType
 mitk::DICOMGDCMImageFrameInfo::GetTagValueAsString(const DICOMTagPath& path) const
 {
   FindingsListType result;
   if (path.Size() == 1 && path.IsExplicit())
   {
     result.push_back(this->GetTagValueAsString(path.GetFirstNode().tag));
   }
   return result;
 }
 
 std::string
 mitk::DICOMGDCMImageFrameInfo
 ::GetFilenameIfAvailable() const
 {
   return this->Filename;
 }
diff --git a/Modules/DICOM/src/mitkDICOMTagBasedSorter.cpp b/Modules/DICOM/src/mitkDICOMTagBasedSorter.cpp
index 1f93af9f62..fc354a7bd6 100644
--- a/Modules/DICOM/src/mitkDICOMTagBasedSorter.cpp
+++ b/Modules/DICOM/src/mitkDICOMTagBasedSorter.cpp
@@ -1,601 +1,601 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkDICOMTagBasedSorter.h"
 
 #include <algorithm>
 #include <iomanip>
 
 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 orientation, let only the first few digits matter (https://phabricator.mitk.org/T12263)
   // 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(m_DefaultStrictSorting)
 ,m_ExpectDistanceOne(m_DefaultExpectDistanceOne)
 {
 }
 
 mitk::DICOMTagBasedSorter
 ::~DICOMTagBasedSorter()
 {
   for(auto ti = m_TagValueProcessor.cbegin();
       ti != m_TagValueProcessor.cend();
       ++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(auto ti = other.m_TagValueProcessor.cbegin();
       ti != other.m_TagValueProcessor.cend();
       ++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(auto ti = other.m_TagValueProcessor.cbegin();
         ti != other.m_TagValueProcessor.cend();
         ++ti)
     {
       m_TagValueProcessor[ti->first] = ti->second->Clone();
     }
   }
   return *this;
 }
 
 bool
 mitk::DICOMTagBasedSorter
 ::operator==(const DICOMDatasetSorter& other) const
 {
   if (const auto* otherSelf = dynamic_cast<const DICOMTagBasedSorter*>(&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 (auto myTag = this->m_DistinguishingTags.cbegin();
         myTag != this->m_DistinguishingTags.cend();
         ++myTag)
     {
       allTagsPresentAndEqual &= (std::find( otherSelf->m_DistinguishingTags.cbegin(), otherSelf->m_DistinguishingTags.cend(), *myTag )
                                  != otherSelf->m_DistinguishingTags.cend()); // 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 "
      << "(strict=" << (m_StrictSorting?"true":"false")
      << ", expectDistanceOne=" << (m_ExpectDistanceOne?"true":"false") << "):"
      << std::endl;
 
   for (auto 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;
 
   if (m_SortCriterion.IsNotNull())
   {
     const DICOMTagList sortingRelevantTags = m_SortCriterion->GetAllTagsOfInterest();
     allTags.insert( allTags.end(), sortingRelevantTags.cbegin(), sortingRelevantTags.cend() ); // append
   }
 
   return allTags;
 }
 
 mitk::DICOMTagList
 mitk::DICOMTagBasedSorter
 ::GetDistinguishingTags() const
 {
   return m_DistinguishingTags;
 }
 
 const mitk::DICOMTagBasedSorter::TagValueProcessor*
 mitk::DICOMTagBasedSorter
 ::GetTagValueProcessorForDistinguishingTag(const DICOMTag& tag) const
 {
   auto loc = m_TagValueProcessor.find(tag);
   if (loc != m_TagValueProcessor.cend())
   {
     return loc->second;
   }
   else
   {
     return nullptr;
   }
 }
 
 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 (auto groupIter = sortedGroups.cbegin();
        groupIter != sortedGroups.cend();
        ++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 (auto tagIter = m_DistinguishingTags.cbegin();
        tagIter != m_DistinguishingTags.cend();
        ++tagIter)
   {
     groupID << tagIter->GetGroup() << tagIter->GetElement(); // make group/element part of the id to cover empty tags
     DICOMDatasetFinding rawTagValue = dataset->GetTagValueAsString(*tagIter);
     std::string processedTagValue;
     if ( m_TagValueProcessor[*tagIter] != nullptr && rawTagValue.isValid)
     {
       processedTagValue = (*m_TagValueProcessor[*tagIter])(rawTagValue.value);
     }
     else
     {
       processedTagValue = rawTagValue.value;
     }
-    groupID << processedTagValue;
+    groupID << "#" << processedTagValue;
   }
   // shorten ID?
   return groupID.str();
 }
 
 mitk::DICOMTagBasedSorter::GroupIDToListType
 mitk::DICOMTagBasedSorter
 ::SplitInputGroups()
 {
   DICOMDatasetList input = GetInput(); // copy
 
   GroupIDToListType listForGroupID;
 
   for (auto dsIter = input.cbegin();
        dsIter != input.cend();
        ++dsIter)
   {
     DICOMDatasetAccess* dataset = *dsIter;
     assert(dataset);
 
-    std::string groupID = this->BuildGroupID( dataset );
+    const 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
     //    - ... ?
 #ifdef MBILOG_ENABLE_DEBUG
     unsigned int groupIndex(0);
 #endif
     for (auto gIter = groups.begin();
          gIter != groups.end();
 #ifdef MBILOG_ENABLE_DEBUG
          ++groupIndex,
 #endif
          ++gIter)
     {
       DICOMDatasetList& dsList = gIter->second;
 
 #ifdef MBILOG_ENABLE_DEBUG
       MITK_DEBUG << "   --------------------------------------------------------------------------------";
       MITK_DEBUG << "   DICOMTagBasedSorter before sorting group : " << groupIndex;
       for (auto oi = dsList.begin();
            oi != dsList.cend();
            ++oi)
       {
         MITK_DEBUG << "     INPUT     : " << (*oi)->GetFilenameIfAvailable();
       }
 #endif // #ifdef MBILOG_ENABLE_DEBUG
 
 
       std::sort( dsList.begin(), dsList.end(), ParameterizedDatasetSort( m_SortCriterion ) );
 
 #ifdef MBILOG_ENABLE_DEBUG
       MITK_DEBUG << "   --------------------------------------------------------------------------------";
       MITK_DEBUG << "   DICOMTagBasedSorter after sorting group : " << groupIndex;
       for (auto oi = dsList.cbegin();
            oi != dsList.cend();
            ++oi)
       {
         MITK_DEBUG << "     OUTPUT    : " << (*oi)->GetFilenameIfAvailable();
       }
       MITK_DEBUG << "   --------------------------------------------------------------------------------";
 
 #endif // MBILOG_ENABLE_DEBUG
 
     }
 
     GroupIDToListType consecutiveGroups;
     if (m_StrictSorting)
     {
       // Step 2: create new groups by enforcing consecutive order within each group
       unsigned int groupIndex(0);
       for (auto gIter = groups.begin();
            gIter != groups.end();
            ++gIter)
       {
         std::stringstream groupKey;
         groupKey << std::setfill('0') << std::setw(6) << groupIndex++;
 
         DICOMDatasetList& dsList = gIter->second;
         DICOMDatasetAccess* previousDS(nullptr);
         unsigned int dsIndex(0);
         double constantDistance(0.0);
         bool constantDistanceInitialized(false);
         for (auto dataset = dsList.cbegin();
              dataset != dsList.cend();
              ++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!
             const 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
 
               // 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)
               {
                 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 (auto gIter = consecutiveGroups.cbegin();
          gIter != consecutiveGroups.cend();
          ++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 (auto firstSlice = firstSlices.cbegin();
          firstSlice != firstSlices.cend();
          ++firstSlice)
     {
       for (auto gIter = consecutiveGroups.cbegin();
            gIter != consecutiveGroups.cend();
            ++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;
   }
 
 #ifdef MBILOG_ENABLE_DEBUG
   unsigned int groupIndex( 0 );
   for ( auto gIter = groups.begin(); gIter != groups.end(); ++groupIndex, ++gIter )
   {
     DICOMDatasetList& dsList = gIter->second;
     MITK_DEBUG << "   --------------------------------------------------------------------------------";
     MITK_DEBUG << "   DICOMTagBasedSorter after sorting group : " << groupIndex;
     for ( auto oi = dsList.begin(); oi != dsList.end(); ++oi )
     {
       MITK_DEBUG << "     OUTPUT    : " << ( *oi )->GetFilenameIfAvailable();
     }
     MITK_DEBUG << "   --------------------------------------------------------------------------------";
   }
 #endif // MBILOG_ENABLE_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/DICOM/src/mitkThreeDnTDICOMSeriesReader.cpp b/Modules/DICOM/src/mitkThreeDnTDICOMSeriesReader.cpp
index a8245c16df..599719fd36 100644
--- a/Modules/DICOM/src/mitkThreeDnTDICOMSeriesReader.cpp
+++ b/Modules/DICOM/src/mitkThreeDnTDICOMSeriesReader.cpp
@@ -1,264 +1,295 @@
 /*============================================================================
 
 The Medical Imaging Interaction Toolkit (MITK)
 
 Copyright (c) German Cancer Research Center (DKFZ)
 All rights reserved.
 
 Use of this source code is governed by a 3-clause BSD license that can be
 found in the LICENSE file.
 
 ============================================================================*/
 
 #include "mitkThreeDnTDICOMSeriesReader.h"
 #include "mitkITKDICOMSeriesReaderHelper.h"
 
 mitk::ThreeDnTDICOMSeriesReader
 ::ThreeDnTDICOMSeriesReader(unsigned int decimalPlacesForOrientation)
 :DICOMITKSeriesGDCMReader(decimalPlacesForOrientation)
 ,m_Group3DandT(m_DefaultGroup3DandT), m_OnlyCondenseSameSeries(m_DefaultOnlyCondenseSameSeries)
 {
 }
 
 mitk::ThreeDnTDICOMSeriesReader
 ::ThreeDnTDICOMSeriesReader(const ThreeDnTDICOMSeriesReader& other )
 :DICOMITKSeriesGDCMReader(other)
 ,m_Group3DandT(m_DefaultGroup3DandT), m_OnlyCondenseSameSeries(m_DefaultOnlyCondenseSameSeries)
 {
 }
 
 mitk::ThreeDnTDICOMSeriesReader
 ::~ThreeDnTDICOMSeriesReader()
 {
 }
 
 mitk::ThreeDnTDICOMSeriesReader&
 mitk::ThreeDnTDICOMSeriesReader
 ::operator=(const ThreeDnTDICOMSeriesReader& other)
 {
   if (this != &other)
   {
     DICOMITKSeriesGDCMReader::operator=(other);
     this->m_Group3DandT = other.m_Group3DandT;
   }
   return *this;
 }
 
 bool
 mitk::ThreeDnTDICOMSeriesReader
 ::operator==(const DICOMFileReader& other) const
 {
   if (const auto* otherSelf = dynamic_cast<const Self*>(&other))
   {
     return
        DICOMITKSeriesGDCMReader::operator==(other)
     && this->m_Group3DandT == otherSelf->m_Group3DandT;
   }
   else
   {
     return false;
   }
 }
 
 void
 mitk::ThreeDnTDICOMSeriesReader
 ::SetGroup3DandT(bool on)
 {
   m_Group3DandT = on;
 }
 
 bool
 mitk::ThreeDnTDICOMSeriesReader
 ::GetGroup3DandT() const
 {
   return m_Group3DandT;
 }
 
+/** Helper function to make the code in mitk::ThreeDnTDICOMSeriesReader
+::Condense3DBlocks(SortingBlockList& resultOf3DGrouping) more readable.*/
+bool BlockShouldBeCondensed(bool onlyCondenseSameSeries, unsigned int currentBlockNumberOfSlices, unsigned int otherBlockNumberOfSlices,
+  const mitk::DICOMDatasetFinding& currentBlockFirstOrigin, const mitk::DICOMDatasetFinding& currentBlockLastOrigin,
+  const mitk::DICOMDatasetFinding& otherBlockFirstOrigin, const mitk::DICOMDatasetFinding& otherBlockLastOrigin,
+  const mitk::DICOMDatasetFinding& currentBlockSeriesInstanceUID, const mitk::DICOMDatasetFinding& otherBlockSeriesInstanceUID)
+{
+  if (otherBlockNumberOfSlices != currentBlockNumberOfSlices)
+    return false; //don't condense blocks that have unequal slice count
+
+  if (!otherBlockFirstOrigin.isValid || !otherBlockLastOrigin.isValid)
+    return false; //don't condense blocks that have invalid origins
+
+  if (!currentBlockFirstOrigin.isValid || !currentBlockLastOrigin.isValid)
+    return false; //don't condense blocks that have invalid origins
+
+  const bool sameSeries = otherBlockSeriesInstanceUID.isValid
+    && currentBlockSeriesInstanceUID.isValid
+    && otherBlockSeriesInstanceUID.value == currentBlockSeriesInstanceUID.value;
+
+  if (onlyCondenseSameSeries && !sameSeries)
+    return false; //don't condense blocks if it is only allowed to condense same series and series are not defined or not equal.
+
+  if (otherBlockFirstOrigin.value != currentBlockFirstOrigin.value)
+    return false; //don't condense blocks that have unequal first origins
+
+  if (otherBlockLastOrigin.value != currentBlockLastOrigin.value)
+    return false; //don't condense blocks that have unequal last origins
+
+  return true;
+}
+
 mitk::DICOMITKSeriesGDCMReader::SortingBlockList
 mitk::ThreeDnTDICOMSeriesReader
 ::Condense3DBlocks(SortingBlockList& resultOf3DGrouping)
 {
   if (!m_Group3DandT)
   {
     return resultOf3DGrouping; // don't work if nobody asks us to
   }
 
   SortingBlockList remainingBlocks = resultOf3DGrouping;
 
   SortingBlockList non3DnTBlocks;
   SortingBlockList true3DnTBlocks;
   std::vector<unsigned int> true3DnTBlocksTimeStepCount;
 
   // we should describe our need for this tag as needed via a function
   // (however, we currently know that the superclass will always need this tag)
   const DICOMTag tagImagePositionPatient(0x0020, 0x0032);
   const DICOMTag tagSeriesInstaceUID(0x0020, 0x000e);
 
   while (!remainingBlocks.empty())
   {
     // new block to fill up
     const DICOMDatasetAccessingImageFrameList& firstBlock = remainingBlocks.front();
     DICOMDatasetAccessingImageFrameList current3DnTBlock = firstBlock;
     int current3DnTBlockNumberOfTimeSteps = 1;
 
     // get block characteristics of first block
     const unsigned int currentBlockNumberOfSlices = firstBlock.size();
-    const std::string currentBlockFirstOrigin = firstBlock.front()->GetTagValueAsString( tagImagePositionPatient ).value;
-    const std::string currentBlockLastOrigin  =  firstBlock.back()->GetTagValueAsString( tagImagePositionPatient ).value;
-    const auto currentBlockSeriesInstanceUID = firstBlock.back()->GetTagValueAsString(tagSeriesInstaceUID).value;
+    const auto currentBlockFirstOrigin = firstBlock.front()->GetTagValueAsString( tagImagePositionPatient );
+    const auto currentBlockLastOrigin  =  firstBlock.back()->GetTagValueAsString( tagImagePositionPatient );
+    const auto currentBlockSeriesInstanceUID = firstBlock.back()->GetTagValueAsString(tagSeriesInstaceUID);
 
     remainingBlocks.erase( remainingBlocks.begin() );
 
     // compare all other blocks against the first one
     for (auto otherBlockIter = remainingBlocks.begin();
          otherBlockIter != remainingBlocks.cend();
          /*++otherBlockIter*/) // <-- inside loop
     {
       // get block characteristics from first block
       const DICOMDatasetAccessingImageFrameList otherBlock = *otherBlockIter;
 
       const unsigned int otherBlockNumberOfSlices = otherBlock.size();
-      const std::string otherBlockFirstOrigin = otherBlock.front()->GetTagValueAsString( tagImagePositionPatient ).value;
-      const std::string otherBlockLastOrigin  =  otherBlock.back()->GetTagValueAsString( tagImagePositionPatient ).value;
-      const auto otherBlockSeriesInstanceUID = otherBlock.back()->GetTagValueAsString(tagSeriesInstaceUID).value;
+      const auto otherBlockFirstOrigin = otherBlock.front()->GetTagValueAsString( tagImagePositionPatient );
+      const auto otherBlockLastOrigin  =  otherBlock.back()->GetTagValueAsString( tagImagePositionPatient );
+      const auto otherBlockSeriesInstanceUID = otherBlock.back()->GetTagValueAsString(tagSeriesInstaceUID);
 
       // add matching blocks to current3DnTBlock
       // keep other blocks for later
-      if (   otherBlockNumberOfSlices == currentBlockNumberOfSlices
-          && (!m_OnlyCondenseSameSeries || otherBlockSeriesInstanceUID == currentBlockSeriesInstanceUID)
-          && otherBlockFirstOrigin == currentBlockFirstOrigin
-          && otherBlockLastOrigin == currentBlockLastOrigin
-          )
+      if ( BlockShouldBeCondensed(m_OnlyCondenseSameSeries, currentBlockNumberOfSlices, otherBlockNumberOfSlices,
+        currentBlockFirstOrigin, currentBlockLastOrigin,
+        otherBlockFirstOrigin, otherBlockLastOrigin,
+        currentBlockSeriesInstanceUID, otherBlockSeriesInstanceUID))
       { // matching block
         ++current3DnTBlockNumberOfTimeSteps;
         current3DnTBlock.insert( current3DnTBlock.end(), otherBlock.begin(), otherBlock.end() ); // append
         // remove this block from remainingBlocks
         otherBlockIter = remainingBlocks.erase(otherBlockIter); // make sure iterator otherBlockIter is valid afterwards
       }
       else
       {
         ++otherBlockIter;
       }
     }
 
     // in any case, we now know all about the first block of our list ...
     // ... and we either call it 3D o 3D+t
     if (current3DnTBlockNumberOfTimeSteps > 1)
     {
       true3DnTBlocks.push_back(current3DnTBlock);
       true3DnTBlocksTimeStepCount.push_back(current3DnTBlockNumberOfTimeSteps);
     }
     else
     {
       non3DnTBlocks.push_back(current3DnTBlock);
     }
   }
 
   // create output for real 3D+t blocks (other outputs will be created by superclass)
   // set 3D+t flag on output block
   this->SetNumberOfOutputs( true3DnTBlocks.size() );
   unsigned int o = 0;
   for (auto blockIter = true3DnTBlocks.cbegin();
        blockIter != true3DnTBlocks.cend();
        ++o, ++blockIter)
   {
     // bad copy&paste code from DICOMITKSeriesGDCMReader, should be handled in a better way
     DICOMDatasetAccessingImageFrameList gdcmFrameInfoList = *blockIter;
     assert(!gdcmFrameInfoList.empty());
 
     // reverse frames if necessary
     // update tilt information from absolute last sorting
     const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmFrameInfoList );
     m_NormalDirectionConsistencySorter->SetInput( datasetList );
     m_NormalDirectionConsistencySorter->Sort();
     const DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( m_NormalDirectionConsistencySorter->GetOutput(0) );
     const GantryTiltInformation& tiltInfo = m_NormalDirectionConsistencySorter->GetTiltInformation();
 
     // set frame list for current block
     const DICOMImageFrameList frameList = ConvertToDICOMImageFrameList( sortedGdcmInfoFrameList );
     assert(!frameList.empty());
 
     DICOMImageBlockDescriptor block;
     block.SetTagCache( this->GetTagCache() ); // important: this must be before SetImageFrameList(), because SetImageFrameList will trigger reading of lots of interesting tags!
     block.SetAdditionalTagsOfInterest(GetAdditionalTagsOfInterest());
     block.SetTagLookupTableToPropertyFunctor(GetTagLookupTableToPropertyFunctor());
     block.SetImageFrameList( frameList );
     block.SetTiltInformation( tiltInfo );
 
     block.SetFlag("3D+t", true);
     block.SetIntProperty("timesteps", true3DnTBlocksTimeStepCount[o]);
     MITK_DEBUG << "Found " << true3DnTBlocksTimeStepCount[o] << " timesteps";
 
     this->SetOutput( o, block );
   }
 
   return non3DnTBlocks;
 }
 
 bool
 mitk::ThreeDnTDICOMSeriesReader
 ::LoadImages()
 {
   bool success = true;
 
   unsigned int numberOfOutputs = this->GetNumberOfOutputs();
   for (unsigned int o = 0; o < numberOfOutputs; ++o)
   {
     const DICOMImageBlockDescriptor& block = this->InternalGetOutput(o);
 
     if (block.GetFlag("3D+t", false))
     {
       success &= this->LoadMitkImageForOutput(o);
     }
     else
     {
       success &= DICOMITKSeriesGDCMReader::LoadMitkImageForOutput(o); // let superclass handle non-3D+t
     }
   }
 
   return success;
 }
 
 bool
 mitk::ThreeDnTDICOMSeriesReader
 ::LoadMitkImageForImageBlockDescriptor(DICOMImageBlockDescriptor& block) const
 {
   PushLocale();
   const DICOMImageFrameList& frames = block.GetImageFrameList();
   const GantryTiltInformation tiltInfo = block.GetTiltInformation();
   const bool hasTilt = tiltInfo.IsRegularGantryTilt();
 
   const int numberOfTimesteps = block.GetNumberOfTimeSteps();
 
   if (numberOfTimesteps == 1)
   {
     return DICOMITKSeriesGDCMReader::LoadMitkImageForImageBlockDescriptor(block);
   }
 
   const int numberOfFramesPerTimestep = block.GetNumberOfFramesPerTimeStep();
 
   ITKDICOMSeriesReaderHelper::StringContainerList filenamesPerTimestep;
   for (int timeStep = 0; timeStep<numberOfTimesteps; ++timeStep)
   {
     // use numberOfFramesPerTimestep frames for a new item in filenamesPerTimestep
     ITKDICOMSeriesReaderHelper::StringContainer filenamesOfThisTimeStep;
     auto timeStepStart = frames.cbegin() + timeStep * numberOfFramesPerTimestep;
     auto timeStepEnd   = frames.cbegin() + (timeStep+1) * numberOfFramesPerTimestep;
     for (auto frameIter = timeStepStart;
         frameIter != timeStepEnd;
         ++frameIter)
     {
       filenamesOfThisTimeStep.push_back( (*frameIter)->Filename );
     }
     filenamesPerTimestep.push_back( filenamesOfThisTimeStep );
   }
 
   mitk::ITKDICOMSeriesReaderHelper helper;
   mitk::Image::Pointer mitkImage = helper.Load3DnT( filenamesPerTimestep, m_FixTiltByShearing && hasTilt, tiltInfo );
 
   block.SetMitkImage( mitkImage );
 
   PopLocale();
 
   return true;
 }