]> Creatis software - gdcm.git/blobdiff - src/gdcmSerieHelper.cxx
Upport itk modif (taken from gdcm1.0) around Jolinda Smith's algorithm
[gdcm.git] / src / gdcmSerieHelper.cxx
index 1c18682fc4cced98ed046e8633db7705f434bdb7..5adbd99b1ffb1c8bea84c73f573438d71cc06f62 100644 (file)
@@ -3,8 +3,8 @@
   Program:   gdcm
   Module:    $RCSfile: gdcmSerieHelper.cxx,v $
   Language:  C++
-  Date:      $Date: 2005/08/30 08:12:40 $
-  Version:   $Revision: 1.18 $
+  Date:      $Date: 2005/11/25 13:56:32 $
+  Version:   $Revision: 1.36 $
                                                                                 
   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
   l'Image). All rights reserved. See Doc/License.txt or
 
 #include <math.h>
 #include <vector>
+#include <map>
 #include <algorithm>
+#include <stdio.h>  //for sscanf
 
 namespace gdcm 
 {
+//-----------------------------------------------------------------------------
 
 //-----------------------------------------------------------------------------
 // Constructor / Destructor
@@ -37,23 +40,9 @@ namespace gdcm
  */
 SerieHelper::SerieHelper()
 {
+   m_UseSeriesDetails = false;
+   ClearAll();
    UserLessThanFunction = 0;
-
-   // For all the File lists that may already exist within the gdcm::Serie
-   FileList *l = GetFirstCoherentFileList();
-   while (l)
-   { 
-      // For all the files of a File list
-      for (gdcm::FileList::iterator it  = l->begin();
-                              it != l->end(); 
-                            ++it)
-      {
-         delete *it; // remove entry
-      }
-      l->clear();
-      delete l;     // remove the list
-      l = GetNextCoherentFileList();
-   }
    DirectOrder = true;
 }
 
@@ -62,20 +51,29 @@ SerieHelper::SerieHelper()
  */
 SerieHelper::~SerieHelper()
 {
-   // For all the Coherent File lists of the gdcm::Serie
-   FileList *l = GetFirstCoherentFileList();
+   ClearAll();
+}
+
+/**
+ * \brief  Preventively, clear everything at constructor time.
+ *         ( use it at destructor time.)
+ */
+void SerieHelper::ClearAll()
+{
+   // For all the 'Single SerieUID' Filesets that may already exist 
+   FileList *l = GetFirstSingleSerieUIDFileSet();
    while (l)
    { 
-      // For all the files of a Coherent File list
-      for (FileList::iterator it  = l->begin();
-                              it != l->end(); 
-                            ++it)
+      // For all the gdcm::File of a File set
+      for (gdcm::FileList::iterator it  = l->begin();
+                                    it != l->end(); 
+                                  ++it)
       {
-         delete *it; // remove entry
+         (*it)->Delete(); // remove each entry
       }
       l->clear();
-      delete l;  // remove the list
-      l = GetNextCoherentFileList();
+      delete l;     // remove the container
+      l = GetNextSingleSerieUIDFileSet();
    }
 }
 
@@ -85,13 +83,13 @@ SerieHelper::~SerieHelper()
 
 // Public
 /**
- * \brief add a gdcm::File to the list corresponding to its Serie UID
+ * \brief add a gdcm::File to the Fileset corresponding to its Serie UID
  * @param   filename Name of the file to deal with
  */
 void SerieHelper::AddFileName(std::string const &filename)
 {
    // Create a DICOM file
-   File *header = new File ();
+   File *header = File::New();
    header->SetLoadMode(LoadMode);
    header->SetFileName( filename ); 
    header->Load();
@@ -99,37 +97,21 @@ void SerieHelper::AddFileName(std::string const &filename)
    if ( header->IsReadable() )
    {
       int allrules = 1;
-      // First step the user has defined a set of rules for the DICOM file
+      // First step : the user defined a set of rules for the DICOM file
       // he is looking for.
-      // make sure the file correspond to his set of rules:
+      // Make sure the file corresponds to his set of rules:
 
-/*
-      for(SerieRestrictions::iterator it = Restrictions.begin();
-          it != Restrictions.end();
-          ++it)
-      {
-         const Rule &r = *it;
-         // doesn't compile (no matching function...).
-         const std::string s;// = header->GetEntryValue( r.first );
-         if ( !Util::DicomStringEqual(s, r.second.c_str()) )
-         {
-           // Argh ! This rule is unmatch let's just quit
-           allrules = 0;
-           break;
-         }
-      }
-*/
-      // Just keep 'new style' for Rules
       std::string s;
       for(SerieExRestrictions::iterator it2 = ExRestrictions.begin();
           it2 != ExRestrictions.end();
           ++it2)
       {
          const ExRule &r = *it2;
-         s = header->GetEntryValue( r.group, r.elem );
+         s = header->GetEntryString( r.group, r.elem );
          if ( !Util::CompareDicomString(s, r.value.c_str(), r.op) )
          {
-           // Argh ! This rule is unmatch let's just quit
+           // Argh ! This rule is unmatched; let's just quit
+
            allrules = 0;
            break;
          }
@@ -137,40 +119,44 @@ void SerieHelper::AddFileName(std::string const &filename)
 
       if ( allrules ) // all rules are respected:
       {
-         // Allright ! we have a found a DICOM that match the user expectation. 
-         // Let's add it !
+         // Allright! we have a found a DICOM that matches the user expectation. 
+         // Let's add it!
 
          // 0020 000e UI REL Series Instance UID
-         const std::string &uid = header->GetEntryValue (0x0020, 0x000e);
+         const std::string &uid = header->GetEntryString(0x0020, 0x000e);
          // if uid == GDCM_UNFOUND then consistently we should find GDCM_UNFOUND
          // no need here to do anything special
 
-         if ( CoherentFileListHT.count(uid) == 0 )
+
+         if ( SingleSerieUIDFileSetHT.count(uid) == 0 )
          {
             gdcmDebugMacro(" New Serie UID :[" << uid << "]");
             // create a std::list in 'uid' position
-            CoherentFileListHT[uid] = new FileList;
+            SingleSerieUIDFileSetHT[uid] = new FileList;
          }
          // Current Serie UID and DICOM header seems to match; add the file:
-         CoherentFileListHT[uid]->push_back( header );
+         SingleSerieUIDFileSetHT[uid]->push_back( header );
       }
       else
       {
-         // at least one rule was unmatch we need to deallocate the file:
-         delete header;
+         // at least one rule was unmatched we need to deallocate the file:
+         header->Delete();
       }
    }
    else
    {
       gdcmWarningMacro("Could not read file: " << filename );
-      delete header;
+      header->Delete();
    }
 }
 
 /**
- * \brief add a gdcm::File to the first (and supposed to be unique) list
+ * \brief add a gdcm::File to the first (and supposed to be unique) file set
  *        of the gdcm::SerieHelper.
  * \warning : this method should be used by aware users only!
+ *           Passing a gdcm::File* has the same effect than passing a file name!
+ * \todo : decide which one is wrong (the method, or the commentary)!
+ *           the following comment doesn't match the method :-(
  *            User is supposed to know the files he want to deal with
  *           and consider them they belong to the same Serie
  *           (even if their Serie UID is different)
@@ -188,12 +174,12 @@ void SerieHelper::AddGdcmFile(File *header)
       // First step the user has defined a set of rules for the DICOM 
       // he is looking for.
       // make sure the file correspond to his set of rules:
-      for(SerieRestrictions::iterator it = Restrictions.begin();
-          it != Restrictions.end();
-          ++it)
+      for(SerieRestrictions::iterator it =  Restrictions.begin();
+                                      it != Restrictions.end();
+                                    ++it)
       {
          const Rule &r = *it;
-         const std::string s;// = header->GetEntryValue( r.first );
+         const std::string s;// = header->GetEntryString( r.first );
          if ( !Util::DicomStringEqual(s, r.second.c_str()) )
          {
            // Argh ! This rule is unmatch let's just quit
@@ -210,14 +196,14 @@ void SerieHelper::AddGdcmFile(File *header)
          // Serie UID of the gdcm::File* may be different.
          // User is supposed to know what he wants
 
-         if ( CoherentFileListHT.count(uid) == 0 )
+         if ( SingleSerieUIDFileSetHT.count(uid) == 0 )
          {
             gdcmDebugMacro(" New Serie UID :[" << uid << "]");
             // create a std::list in 'uid' position
-            CoherentFileListHT[uid] = new FileList;
+            SingleSerieUIDFileSetHT[uid] = new FileList;
          }
          // Current Serie UID and DICOM header seems to match; add the file:
-         CoherentFileListHT[uid]->push_back( header );
+         SingleSerieUIDFileSetHT[uid]->push_back( header );
       }
          // Even if a rule was unmatch we don't deallocate the gdcm::File:
 }
@@ -228,41 +214,25 @@ void SerieHelper::AddGdcmFile(File *header)
  * directory which would have a particular EchoTime==4.0.
  * This method is a user level, value is not required to be formatted as a DICOM
  * string
+ * @param   key  Target tag we want restrict on a given value
+ * @param value value to be checked to exclude File
+ * @param op  operator we want to use to check
  */
-void SerieHelper::AddRestriction(uint16_t group, uint16_t elem
+void SerieHelper::AddRestriction(TagKey const &key
                                  std::string const &value, int op)
 {
    ExRule r;
-   r.group = group;
-   r.elem  = elem;
+   r.group = key[0];
+   r.elem  = key[1];
    r.value = value;
    r.op    = op;
    ExRestrictions.push_back( r ); 
 }
 
-#ifndef GDCM_LEGACY_REMOVE
-/**
- * \brief add a rules for restricting a DICOM file to be in the serie we are
- * trying to find. For example you can select only the DICOM file from a
- * directory which would have a particular EchoTime==4.0.
- * This method is a user level, value is not required to be formatted as a DICOM
- * string
- * @deprecated use : AddRestriction(uint16_t group, uint16_t elem, 
- *                                 std::string const &value, int op);
- */
-void SerieHelper::AddRestriction(TagKey const &key, std::string const &value)
-{
-   Rule r;
-   r.first = key;
-   r.second = value;
-   Restrictions.push_back( r ); 
-}
-#endif
-
 /**
  * \brief Sets the root Directory
  * @param   dir Name of the directory to deal with
- * @param recursive whether we want explore recursively the Directory
+ * @param recursive whether we want explore recursively the root Directory
  */
 void SerieHelper::SetDirectory(std::string const &dir, bool recursive)
 {
@@ -277,30 +247,31 @@ void SerieHelper::SetDirectory(std::string const &dir, bool recursive)
 }
 
 /**
- * \brief Sorts the given File List
+ * \brief Sorts the given Fileset
  * \warning This could be implemented in a 'Strategy Pattern' approach
  *          But as I don't know how to do it, I leave it this way
- *          BTW, this is also a Strategy, I don't know this is the best approach :)
+ *          BTW, this is also a Strategy, I don't know this is 
+ *          the best approach :)
  */
-void SerieHelper::OrderFileList(FileList *coherentFileList)
+void SerieHelper::OrderFileList(FileList *fileSet)
 {
 
    if ( SerieHelper::UserLessThanFunction )
    {
-      UserOrdering( coherentFileList );
+      UserOrdering( fileSet );
       return; 
    }
-   else if ( ImagePositionPatientOrdering( coherentFileList ) )
+   else if ( ImagePositionPatientOrdering( fileSet ) )
    {
       return ;
    }
-   else if ( ImageNumberOrdering(coherentFileList ) )
+   else if ( ImageNumberOrdering(fileSet ) )
    {
       return ;
    }
    else  
    {
-      FileNameOrdering(coherentFileList );
+      FileNameOrdering(fileSet );
    }
 }
 
@@ -308,20 +279,20 @@ void SerieHelper::OrderFileList(FileList *coherentFileList)
  * \brief Elementary coherence checking of the files with the same Serie UID
  * Only sizes and pixel type are checked right now ...
  */ 
-bool SerieHelper::IsCoherent(FileList *coherentFileList)
+bool SerieHelper::IsCoherent(FileList *fileSet)
 {
-   if(coherentFileList->size() == 1)
+   if(fileSet->size() == 1)
    return true;
 
-   FileList::const_iterator it = coherentFileList->begin();
-
-   int nX = (*it)->GetXSize();
-   int nY = (*it)->GetYSize();
-   int pixelSize = (*it)->GetPixelSize();
+   FileList::const_iterator it = fileSet->begin();
 
+   int nX =               (*it)->GetXSize();
+   int nY =               (*it)->GetYSize();
+   int pixelSize =        (*it)->GetPixelSize();
+   bool signedPixelData = (*it)->IsSignedPixelData();
    it ++;
    for ( ;
-         it != coherentFileList->end();
+         it != fileSet->end();
        ++it)
    {
       if ( (*it)->GetXSize() != nX )
@@ -330,47 +301,250 @@ bool SerieHelper::IsCoherent(FileList *coherentFileList)
          return false;
       if ( (*it)->GetPixelSize() != pixelSize )
          return false;
-      // probabely more is to be checked (?)
+      if ( (*it)->IsSignedPixelData() != signedPixelData )
+         return false;
+      // probabely more is to be checked (?)      
    }
    return true;
 }
+
+#ifndef GDCM_LEGACY_REMOVE
+
+FileList *SerieHelper::GetFirstCoherentFileList()
+{
+   ItFileSetHt = SingleSerieUIDFileSetHT.begin();
+   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
+      return ItFileSetHt->second;
+   return NULL;
+}
+
+
+FileList *SerieHelper::GetNextCoherentFileList()
+{
+   gdcmAssertMacro (ItFileSetHt != SingleSerieUIDFileSetHT.end());
+  
+   ++ItFileSetHt;
+   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
+      return ItFileSetHt->second;
+   return NULL;
+}
+
+
+FileList *SerieHelper::GetCoherentFileList(std::string SerieUID)
+{
+   if ( SingleSerieUIDFileSetHT.count(SerieUID) == 0 )
+      return 0;     
+   return SingleSerieUIDFileSetHT[SerieUID];
+}
+#endif
+
+
 /**
- * \brief   Get the first List while visiting the CoherentFileListHT
- * @return  The first FileList if found, otherwhise NULL
+ * \brief   Get the first Fileset while visiting the SingleSerieUIDFileSetmap
+ * @return  The first FileList (SingleSerieUIDFileSet) if found, otherwhise 0
  */
-FileList *SerieHelper::GetFirstCoherentFileList()
+FileList *SerieHelper::GetFirstSingleSerieUIDFileSet()
 {
-   ItListHt = CoherentFileListHT.begin();
-   if ( ItListHt != CoherentFileListHT.end() )
-      return ItListHt->second;
+   ItFileSetHt = SingleSerieUIDFileSetHT.begin();
+   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
+      return ItFileSetHt->second;
    return NULL;
 }
 
 /**
- * \brief   Get the next List while visiting the CoherentFileListHT
- * \note : meaningfull only if GetFirstCoherentFileList() already called
- * @return  The next FileList if found, otherwhise NULL
+ * \brief   Get the next Fileset while visiting the SingleSerieUIDFileSetmap
+ * \note : meaningfull only if GetNextSingleSerieUIDFileSet() already called 
+ * @return  The next FileList (SingleSerieUIDFileSet) if found, otherwhise 0
  */
-FileList *SerieHelper::GetNextCoherentFileList()
+FileList *SerieHelper::GetNextSingleSerieUIDFileSet()
 {
-   gdcmAssertMacro (ItListHt != CoherentFileListHT.end());
+   gdcmAssertMacro (ItFileSetHt != SingleSerieUIDFileSetHT.end());
   
-   ++ItListHt;
-   if ( ItListHt != CoherentFileListHT.end() )
-      return ItListHt->second;
+   ++ItFileSetHt;
+   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
+      return ItFileSetHt->second;
    return NULL;
 }
 
 /**
- * \brief   Get the Coherent Files list according to its Serie UID
- * @param SerieUID SerieUID
- * \return  pointer to the Coherent Files list if found, otherwhise NULL
+ * \brief   Get the SingleSerieUIDFileSet according to its Serie UID
+ * @param SerieUID SerieUID to retrieve
+ * \return pointer to the FileList (SingleSerieUIDFileSet) if found, otherwhise 0
  */
-FileList *SerieHelper::GetCoherentFileList(std::string SerieUID)
+FileList *SerieHelper::GetSingleSerieUIDFileSet(std::string SerieUID)
 {
-   if ( CoherentFileListHT.count(SerieUID) == 0 )
+   if ( SingleSerieUIDFileSetHT.count(SerieUID) == 0 )
       return 0;     
-   return CoherentFileListHT[SerieUID];
+   return SingleSerieUIDFileSetHT[SerieUID];
+}
+
+/**
+ * \brief   Splits a Single SerieUID Fileset according to the Orientations
+ * @param fileSet File Set to be splitted
+ * \return  std::map of 'Xcoherent' File sets
+ */
+
+XCoherentFileSetmap SerieHelper::SplitOnOrientation(FileList *fileSet)
+{
+   XCoherentFileSetmap CoherentFileSet;
+
+   int nb = fileSet->size();
+   if (nb == 0 )
+      return CoherentFileSet;
+   double iop[6];
+
+   std::string strOrient;
+   std::ostringstream ossOrient;   
+   FileList::const_iterator it = fileSet->begin();
+   it ++;
+   for ( ;
+         it != fileSet->end();
+       ++it)
+   {     
+      // Information is in :      
+      // 0020 0037 : Image Orientation (Patient) or
+      // 0020 0035 : Image Orientation (RET)
+
+      // Let's build again the 'cosines' string, to be sure of it's format      
+      (*it)->GetImageOrientationPatient(iop);
+
+      ossOrient << iop[0];      
+      for (int i = 1; i < 6; i++)
+      {
+        ossOrient << "\\";
+        ossOrient << iop[i]; 
+      }      
+      strOrient = ossOrient.str();
+      ossOrient.str("");
+      // FIXME : is it a 'cleaner' way to initialize an ostringstream? 
+
+      if ( CoherentFileSet.count(strOrient) == 0 )
+      {
+         gdcmDebugMacro(" New Orientation :[" << strOrient << "]");
+         // create a File set in 'orientation' position
+         CoherentFileSet[strOrient] = new FileList;
+      }
+      // Current Orientation and DICOM header match; add the file:
+      CoherentFileSet[strOrient]->push_back( (*it) );
+   } 
+   return CoherentFileSet;
+}
+
+/**
+ * \brief   Splits a 'Single SerieUID' Fileset according to the Positions
+ * @param fileSet File Set to be splitted
+ * \return  std::map of 'Xcoherent' File sets
+ */
+
+XCoherentFileSetmap SerieHelper::SplitOnPosition(FileList *fileSet)
+{
+   XCoherentFileSetmap CoherentFileSet;
+
+   int nb = fileSet->size();
+   if (nb == 0 )
+      return CoherentFileSet;
+   float pos[3];
+   std::string strImPos;  // read on disc
+   std::ostringstream ossPosition;
+   std::string strPosition; // re computed
+   FileList::const_iterator it = fileSet->begin();
+   it ++;
+   for ( ;
+         it != fileSet->end();
+       ++it)
+   {     
+      // Information is in :      
+      // 0020,0032 : Image Position Patient
+      // 0020,0030 : Image Position (RET)
+
+      strImPos = (*it)->GetEntryString(0x0020,0x0032);
+      if ( strImPos == GDCM_UNFOUND)
+      {
+         gdcmWarningMacro( "Unfound Image Position Patient (0020,0032)");
+         strImPos = (*it)->GetEntryString(0x0020,0x0030); // For ACR-NEMA images
+         if ( strImPos == GDCM_UNFOUND )
+         {
+            gdcmWarningMacro( "Unfound Image Position (RET) (0020,0030)");
+            // User wants to split on the 'Position'
+            // No 'Position' info found.
+            // We return an empty Htable !
+            return CoherentFileSet;
+         }  
+      }
+
+      if ( sscanf( strImPos.c_str(), "%f \\%f \\%f ", 
+                                              &pos[0], &pos[1], &pos[2]) != 3 )
+      {
+            gdcmWarningMacro( "Wrong number for Position : ["
+                       << strImPos << "]" );
+             return CoherentFileSet;
+      }
+
+      // Let's build again the 'position' string, to be sure of it's format      
+
+      ossPosition << pos[0];      
+      for (int i = 1; i < 3; i++)
+      {
+        ossPosition << "\\";
+        ossPosition << pos[i]; 
+      }      
+      strPosition = ossPosition.str();
+      ossPosition.str("");
+            
+      if ( CoherentFileSet.count(strPosition) == 0 )
+      {
+         gdcmDebugMacro(" New Position :[" << strPosition << "]");
+         // create a File set in 'position' position
+         CoherentFileSet[strPosition] = new FileList;
+      }
+      // Current Position and DICOM header match; add the file:
+      CoherentFileSet[strPosition]->push_back( (*it) );
+   }   
+   return CoherentFileSet;
+}
+
+/**
+ * \brief   Splits a 'Single SerieUID' File set Coherent according to the
+ *          value of a given Tag
+ * @param fileSet File Set to be splitted
+ * @param   group  group number of the target Element
+ * @param   elem element number of the target Element
+ * \return  std::map of 'Xcoherent' File sets
+ */
+
+XCoherentFileSetmap SerieHelper::SplitOnTagValue(FileList *fileSet, 
+                                               uint16_t group, uint16_t elem)
+{
+   XCoherentFileSetmap CoherentFileSet;
+
+   int nb = fileSet->size();
+   if (nb == 0 )
+      return CoherentFileSet;
+
+   std::string strTagValue;  // read on disc
+
+   FileList::const_iterator it = fileSet->begin();
+   it ++;
+   for ( ;
+         it != fileSet->end();
+       ++it)
+   {     
+      // Information is in :      
+      // 0020,0032 : Image Position Patient
+      // 0020,0030 : Image Position (RET)
+
+      strTagValue = (*it)->GetEntryString(group,elem);
+      
+      if ( CoherentFileSet.count(strTagValue) == 0 )
+      {
+         gdcmDebugMacro(" New Tag Value :[" << strTagValue << "]");
+         // create a File set in 'position' position
+         CoherentFileSet[strTagValue] = new FileList;
+      }
+      // Current Tag value and DICOM header match; add the file:
+      CoherentFileSet[strTagValue]->push_back( (*it) );
+   }
+   return CoherentFileSet;
 }
 
 //-----------------------------------------------------------------------------
@@ -379,12 +553,14 @@ FileList *SerieHelper::GetCoherentFileList(std::string SerieUID)
 //-----------------------------------------------------------------------------
 // Private
 /**
- * \brief sorts the images, according to their Patient Position
+ * \brief sorts the images, according to their Patient Position.
+ *
  *  We may order, considering :
  *   -# Image Position Patient
  *   -# Image Number
+ *   -# file name
  *   -# More to come :-)
- * WARNING : FileList = std::vector<File* >
+ * \note : FileList = std::vector<File* >
  * @param fileList Coherent File list (same Serie UID) to sort
  * @return false only if the header is bugged !
  */
@@ -392,16 +568,15 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList )
 //based on Jolinda Smith's algorithm
 {
    //iop is calculated based on the file file
-   float cosines[6];
-   float normal[3];
-   float ipp[3];
-   float dist;
-   float min = 0, max = 0;
+   double cosines[6];
+   double normal[3];
+   double ipp[3];
+   double dist;
+   double min = 0, max = 0;
    bool first = true;
-   int n=0;
-   std::vector<float> distlist;
 
-   //!\todo rewrite this for loop.
+   std::multimap<double,File *> distmultimap;
+   // Use a multimap to sort the distances from 0,0,0
    for ( FileList::const_iterator 
          it = fileList->begin();
          it != fileList->end(); ++it )
@@ -412,8 +587,8 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList )
       
          // You only have to do this once for all slices in the volume. Next, 
          // for each slice, calculate the distance along the slice normal 
-         // using the IPP tag ("dist" is initialized to zero before reading 
-         // the first slice) :
+         // using the IPP ("Image Position Patient") tag.
+         // ("dist" is initialized to zero before reading the first slice) :
          normal[0] = cosines[1]*cosines[5] - cosines[2]*cosines[4];
          normal[1] = cosines[2]*cosines[3] - cosines[0]*cosines[5];
          normal[2] = cosines[0]*cosines[4] - cosines[1]*cosines[3];
@@ -428,7 +603,7 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList )
             dist += normal[i]*ipp[i];
          }
     
-         distlist.push_back( dist );
+         distmultimap.insert(std::pair<const double,File *>(dist, *it));
 
          max = min = dist;
          first = false;
@@ -445,79 +620,66 @@ bool SerieHelper::ImagePositionPatientOrdering( FileList *fileList )
             dist += normal[i]*ipp[i];
          }
 
-         distlist.push_back( dist );
+         distmultimap.insert(std::pair<const double,File *>(dist, *it));
 
          min = (min < dist) ? min : dist;
          max = (max > dist) ? max : dist;
       }
-      ++n;
-   }
 
-   // Then I order the slices according to the value "dist". Finally, once
-   // I've read in all the slices, I calculate the z-spacing as the difference
-   // between the "dist" values for the first two slices.
-   FileVector CoherentFileVector(n);
-   // CoherentFileVector.reserve( n );
-   CoherentFileVector.resize( n );
-   // gdcmAssertMacro( CoherentFileVector.capacity() >= n );
+   }
 
    // Find out if min/max are coherent
    if ( min == max )
-     {
-     gdcmWarningMacro( "Looks like all images have the exact same image position."
-                       << "No PositionPatientOrdering sort performed" );
-     return false;
-     }
-
-   float step = (max - min)/(n - 1);
-   int pos;
-   n = 0;
-    
-   //VC++ don't understand what scope is !! it -> it2
-   for (FileList::const_iterator it2  = fileList->begin();
-        it2 != fileList->end(); ++it2, ++n)
    {
-      //2*n sort algo !!
-      //Assumption: all files are present (no one missing)
-      pos = (int)( fabs( (distlist[n]-min)/step) + .5 );
-
-      // a Dicom 'Serie' may contain scout views
-      // and images may have differents directions
-      // -> More than one may have the same 'pos'
-      // Sorting has then NO meaning !
-      if (CoherentFileVector[pos]==NULL)
-         CoherentFileVector[pos] = *it2;
-      else
-      {
-         gdcmWarningMacro( "At least 2 files with same position. No PositionPatientOrdering sort performed");
-         return false;
-      }
+     gdcmWarningMacro("Looks like all images have the exact same image position"
+                      << ". No PositionPatientOrdering sort performed" );
+     return false;
    }
 
+   // Check to see if image shares a common position
+    bool ok = true;
+    for (std::multimap<double, File *>::iterator it2 = distmultimap.begin();
+                                                 it2 != distmultimap.end();
+                                                 ++it2)
+    {
+       if (distmultimap.count((*it2).first) != 1)
+       {
+          gdcmErrorMacro("File: "
+               << ((*it2).second->GetFileName())
+               << " Distance: "
+               << (*it2).first
+               << " position is not unique");
+          ok = false;
+       } 
+    }
+    if (!ok)
+    {
+       return false;
+    }
+  
    fileList->clear();  // doesn't delete list elements, only nodes
 
    if (DirectOrder)
    {  
-      //VC++ don't understand what scope is !! it -> it3
-      for (FileVector::const_iterator it3  = CoherentFileVector.begin();
-           it3 != CoherentFileVector.end(); ++it3)
+      for (std::multimap<double, File *>::iterator it3 = distmultimap.begin();
+                                                   it3 != distmultimap.end();
+                                                 ++it3)
       {
-         fileList->push_back( *it3 );
+         fileList->push_back( (*it3).second );
       }
    }
    else // user asked for reverse order
    {
-      FileVector::const_iterator it4;
-      it4 = CoherentFileVector.end();
+      std::multimap<double, File *>::const_iterator it4;
+      it4 = distmultimap.end();
       do
       {
          it4--;
-         fileList->push_back( *it4 );
-      } while (it4 != CoherentFileVector.begin() );
+         fileList->push_back( (*it4).second );
+      } while (it4 != distmultimap.begin() );
    } 
 
-   distlist.clear();
-   CoherentFileVector.clear();
+   distmultimap.clear();
 
    return true;
 }
@@ -531,12 +693,13 @@ bool SerieHelper::ImageNumberGreaterThan(File *file1, File *file2)
 {
   return file1->GetImageNumber() > file2->GetImageNumber();
 }
+
 /**
  * \brief sorts the images, according to their Image Number
  * \note Works only on bona fide files  (i.e image number is a character string
  *                                      corresponding to an integer)
  *             within a bona fide serie (i.e image numbers are consecutive)
- * @param fileList Coherent File list (same Serie UID) to sort 
+ * @param fileList  File set (same Serie UID) to sort 
  * @return false if non bona fide stuff encountered
  */
 bool SerieHelper::ImageNumberOrdering(FileList *fileList) 
@@ -557,25 +720,28 @@ bool SerieHelper::ImageNumberOrdering(FileList *fileList)
    // Find out if image numbers are coherent (consecutive)
    if ( min == max || max == 0 || max >= (n+min) )
    {
-      gdcmWarningMacro( " 'Image numbers' not coherent. No ImageNumberOrdering sort performed.");
+      gdcmWarningMacro( " 'Image numbers' not coherent. "
+                        << " No ImageNumberOrdering sort performed.");
       return false;
    }
    if (DirectOrder) 
-      std::sort(fileList->begin(), fileList->end(), SerieHelper::ImageNumberLessThan );
+      std::sort(fileList->begin(), fileList->end(), 
+                                          SerieHelper::ImageNumberLessThan );
    else
-      std::sort(fileList->begin(), fileList->end(), SerieHelper::ImageNumberGreaterThan );
+      std::sort(fileList->begin(), fileList->end(),
+                                          SerieHelper::ImageNumberGreaterThan );
 
    return true;
 }
 
 bool SerieHelper::FileNameLessThan(File *file1, File *file2)
 {
-  return file1->GetFileName() < file2->GetFileName();
+   return file1->GetFileName() < file2->GetFileName();
 }
 
 bool SerieHelper::FileNameGreaterThan(File *file1, File *file2)
 {
-  return file1->GetFileName() > file2->GetFileName();
+   return file1->GetFileName() > file2->GetFileName();
 }
 /**
  * \brief sorts the images, according to their File Name
@@ -585,28 +751,122 @@ bool SerieHelper::FileNameGreaterThan(File *file1, File *file2)
 bool SerieHelper::FileNameOrdering(FileList *fileList)
 {
    if (DirectOrder) 
-      std::sort(fileList->begin(), fileList->end(), SerieHelper::FileNameLessThan);
+      std::sort(fileList->begin(), fileList->end(), 
+                                       SerieHelper::FileNameLessThan);
    else
-      std::sort(fileList->begin(), fileList->end(), SerieHelper::FileNameGreaterThan);
+      std::sort(fileList->begin(), fileList->end(), 
+                                       SerieHelper::FileNameGreaterThan);
 
    return true;
 }
 
 /**
  * \brief sorts the images, according to user supplied function
- * \note Only Direct ordering is allowed
  * @param fileList Coherent File list (same Serie UID) to sort
  * @return false only if the header is bugged !
  */
 bool SerieHelper::UserOrdering(FileList *fileList)
 {
-   if (DirectOrder) 
-      std::sort(fileList->begin(), fileList->end(), SerieHelper::UserLessThanFunction);
-   else
-      gdcmWarningMacro( " Only Direct ordering allowed "
-                     << "when user function is supplied");
+   std::sort(fileList->begin(), fileList->end(), 
+                                    SerieHelper::UserLessThanFunction);
+   if (!DirectOrder) 
+   {
+      std::reverse(fileList->begin(), fileList->end());
+   }
    return true;
 }
+
+std::string SerieHelper::CreateUniqueSeriesIdentifier( File * inFile )
+{
+   if( inFile->IsReadable() )
+   {
+     // 0020 000e UI REL Series Instance UID
+    std::string uid =  inFile->GetEntryString (0x0020, 0x000e);
+    std::string id = uid.c_str();
+    if(m_UseSeriesDetails)
+    {
+    // If the user requests, additional information can be appended
+    // to the SeriesUID to further differentiate volumes in the DICOM
+    // objects being processed.
+   // 0020 0011 Series Number
+   // A scout scan prior to a CT volume scan can share the same
+   // SeriesUID, but they will sometimes have a different Series Number
+       std::string sNum = inFile->GetEntryString(0x0020, 0x0011);
+       if( sNum == gdcm::GDCM_UNFOUND )
+       {
+           sNum = "";
+       }
+ // 0018 0024 Sequence Name
+ // For T1-map and phase-contrast MRA, the different flip angles and
+ // directions are only distinguished by the Sequence Name
+         std::string sName = inFile->GetEntryString(0x0018, 0x0024);
+         if( sName == gdcm::GDCM_UNFOUND )
+         {
+           sName = "";
+         }
+  // 0018 0050 Slice Thickness
+  // On some CT systems, scout scans and subsequence volume scans will
+  // have the same SeriesUID and Series Number - YET the slice
+  // thickness will differ from the scout slice and the volume slices.
+         std::string sThick = inFile->GetEntryString (0x0018, 0x0050);
+         if( sThick == gdcm::GDCM_UNFOUND )
+         {
+           sThick = "";
+         }
+  // 0028 0010 Rows
+  // If the 2D images in a sequence don't have the same number of rows,
+  // then it is difficult to reconstruct them into a 3D volume.
+         std::string sRows = inFile->GetEntryString (0x0028, 0x0010);
+         if( sRows == gdcm::GDCM_UNFOUND )
+         {
+           sRows = "";
+         }
+  // 0028 0011 Columns
+  // If the 2D images in a sequence don't have the same number of columns,
+  // then it is difficult to reconstruct them into a 3D volume.
+         std::string sColumns = inFile->GetEntryString (0x0028, 0x0011);
+         if( sColumns == gdcm::GDCM_UNFOUND )
+         {
+           sColumns = "";
+         }
+   
+  // Concat the new info
+         std::string num = sNum.c_str();
+         num += sName.c_str();
+         num += sThick.c_str();
+         num += sRows.c_str();
+         num += sColumns.c_str();
+   
+  // Append the new info to the SeriesUID
+         id += ".";
+         id += num.c_str();
+       }
+   
+  // Eliminate non-alnum characters, including whitespace...
+  // that may have been introduced by concats.
+       for(unsigned int i=0; i<id.size(); i++)
+       {
+         while(i<id.size()
+               && !( id[i] == '.'
+                    || (id[i] >= 'a' && id[i] <= 'z')
+                    || (id[i] >= '0' && id[i] <= '9')
+                    || (id[i] >= 'A' && id[i] <= 'Z')))
+         {
+           id.erase(i, 1);
+         }
+       }
+       return id;
+     }
+     else // Could not open inFile
+     {
+       gdcmWarningMacro("Could not parse series info.");
+       std::string id = gdcm::GDCM_UNFOUND;
+       return id;
+     }
+}
+
+
 //-----------------------------------------------------------------------------
 // Print
 /**
@@ -615,17 +875,17 @@ bool SerieHelper::UserOrdering(FileList *fileList)
 void SerieHelper::Print(std::ostream &os, std::string const &indent)
 {
    // For all the Coherent File lists of the gdcm::Serie
-   CoherentFileListmap::iterator itl = CoherentFileListHT.begin();
-   if ( itl == CoherentFileListHT.end() )
+   SingleSerieUIDFileSetmap::iterator itl = SingleSerieUIDFileSetHT.begin();
+   if ( itl == SingleSerieUIDFileSetHT.end() )
    {
-      gdcmWarningMacro( "No Coherent File list found" );
+      gdcmWarningMacro( "No SingleSerieUID File set found" );
       return;
    }
-   while (itl != CoherentFileListHT.end())
+   while (itl != SingleSerieUIDFileSetHT.end())
    { 
       os << "Serie UID :[" << itl->first << "]" << std::endl;
 
-      // For all the files of a Coherent File list
+      // For all the files of a SingleSerieUID File set
       for (FileList::iterator it =  (itl->second)->begin();
                                   it != (itl->second)->end(); 
                                 ++it)