⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gdcmseriehelper.cxx

📁 DTMK软件开发包,此为开源软件,是一款很好的医学图像开发资源.
💻 CXX
📖 第 1 页 / 共 2 页
字号:
/*=========================================================================
                                                                                
  Program:   gdcm
  Module:    $RCSfile: gdcmSerieHelper.cxx,v $
  Language:  C++
  Date:      $Date: 2007-06-18 10:20:26 $
  Version:   $Revision: 1.25 $
                                                                                
  Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
  l'Image). All rights reserved. See Doc/License.txt or
  http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
                                                                                
     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.
                                                                                
=========================================================================*/

#include "gdcmSerieHelper.h"
#include "gdcmDirList.h"
#include "gdcmFile.h"
#include "gdcmDictEntry.h" // for TranslateToKey
#include "gdcmDebug.h"
#include "gdcmUtil.h"

#include <math.h>
#include <vector>
#include <algorithm>
#include <map>
#include <stdio.h>  //for sscanf

namespace gdcm
{

//-----------------------------------------------------------------------------
// Constructor / Destructor
/**
 * \brief   Constructor from a given SerieHelper
 */
SerieHelper::SerieHelper()
{
   m_UseSeriesDetails = false;
   ClearAll();
   UserLessThanFunction = 0;
   DirectOrder = true;
   LoadMode = 0;
}

/**
 * \brief   Canonical destructor.
 */
SerieHelper::~SerieHelper()
{
   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 gdcm::File of a File set
      for (gdcm::FileList::iterator it  = l->begin();
                                    it != l->end(); 
                                  ++it)
      {
         delete *it; // remove each entry
      }
      l->clear();
      delete l;     // remove the container
      l = GetNextSingleSerieUIDFileSet();
   }
   // Need to clear that too:
   SingleSerieUIDFileSetHT.clear();
}

//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------

// Public
/**
 * \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 ();
   header->SetLoadMode(LoadMode);
   header->SetFileName( filename ); 
   header->Load();

   if ( header->IsReadable() )
   {
      if ( !AddFile( header ) )
      {
         // at least one rule was unmatched we need to deallocate the file:
         delete header;
      }
   }
   else
   {
      gdcmWarningMacro("Could not read file: " << filename );
      delete header;
   }
}

/**
 * \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)
 *           user will probabely OrderFileList() this list (actually, ordering
 *           user choosen gdm::File is the sole interest of this method)
 *           Moreover, using vtkGdcmReader::SetCoherentFileList() will avoid
 *           vtkGdcmReader parsing twice the same files. 
 *           *no* coherence check is performed, but those specified
 *           by SerieHelper::AddRestriction()
 * @param   header gdcm::File* of the file to deal with
 * @return  true if file was added, false if file was rejected
 */
bool SerieHelper::AddFile(File *header)
{
   int allrules = 1;
   // 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:

   std::string s;
   for(SerieExRestrictions::iterator it2 = ExRestrictions.begin();
     it2 != ExRestrictions.end();
     ++it2)
   {
      const ExRule &r = *it2;
      s = header->GetEntryValue( r.group, r.elem );
      if ( !Util::CompareDicomString(s, r.value.c_str(), r.op) )
      {
         // Argh ! This rule is unmatched; let's just quit
         allrules = 0;
         break;
      }
   }

   if ( allrules ) // all rules are respected:
   {
      // Allright! we have a found a DICOM that matches the user expectation. 
      // Let's add it to the specific 'id' which by default is uid (Serie UID)
      // but can be `refined` by user with more paramater (see AddRestriction(g,e))
 
      std::string id = CreateUniqueSeriesIdentifier( header );
      // if id == GDCM_UNFOUND then consistently we should find GDCM_UNFOUND
      // no need here to do anything special
 
      if ( SingleSerieUIDFileSetHT.count(id) == 0 )
      {
         gdcmDebugMacro(" New Serie UID :[" << id << "]");
         // create a std::list in 'id' position
         SingleSerieUIDFileSetHT[id] = new FileList;
      }
      // Current Serie UID and DICOM header seems to match add the file:
      SingleSerieUIDFileSetHT[id]->push_back( header );
   }
   else
   {
      // one rule not matched, tell user:
      return false;
   }
   return true;
}

/**
 * \brief add a rule for restricting a DICOM file to be in the serie we are
 * trying to find. For example you can select only the DICOM files 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
 * \todo find a trick to allow user to say if he wants the Rectrictions 
 *       to be *ored* (and not only *anded*)
 * @param   group  Group number of the target tag.
 * @param   elem Element number of the target tag.
 * @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, 
                                 std::string const &value, int op)
{
   ExRule r;
   r.group = group;
   r.elem  = elem;
   r.value = value;
   r.op    = op;
   ExRestrictions.push_back( r ); 
}

void SerieHelper::AddRestriction(uint16_t group, uint16_t elem)
{
  ExRule r;
  r.group = group;
  r.elem  = elem;
  ExRefine.push_back( r );
}

#ifndef GDCM_LEGACY_REMOVE
/**
 * \brief add a rule for restricting a DICOM file to be in the serie we are
 * trying to find. For example you can select only the DICOM files 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
 * @param   group  Group number of the target tag.
 * @param   elem Element number of the target tag.
 * @param value value to be checked to exclude File 
 * @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 root Directory
 */
void SerieHelper::SetDirectory(std::string const &dir, bool recursive)
{
   DirList dirList(dir, recursive); // OS specific
  
   DirListType filenames_list = dirList.GetFilenames();
   for( DirListType::const_iterator it = filenames_list.begin(); 
        it != filenames_list.end(); ++it)
   {
      AddFileName( *it );
   }
}

/**
 * \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 :)
 */
void SerieHelper::OrderFileList(FileList *fileSet)
{

   if ( SerieHelper::UserLessThanFunction )
   {
      UserOrdering( fileSet );
      return; 
   }
   else if ( ImagePositionPatientOrdering( fileSet ) )
   {
      return ;
   }
   else if ( ImageNumberOrdering(fileSet ) )
   {
      return ;
   }
   else  
   {
      FileNameOrdering(fileSet );
   }
}

/**
 * \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 *fileSet)
{
   if(fileSet->size() == 1)
   return true;

   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 != fileSet->end();
       ++it)
   {
      if ( (*it)->GetXSize() != nX )
         return false;
      if ( (*it)->GetYSize() != nY )
         return false;
      if ( (*it)->GetPixelSize() != pixelSize )
         return false;
      if ( (*it)->IsSignedPixelData() != signedPixelData )
         return false;
      // probabely more is to be checked (?)
   }
   return true;
}

#ifndef GDCM_LEGACY_REMOVE
/**
 * \brief   accessor (DEPRECATED :  use GetFirstSingleSerieUIDFileSet )
 *          Warning : 'coherent' means here they have the same Serie UID
 * @return  The first FileList if found, otherwhise NULL
 */
FileList *SerieHelper::GetFirstCoherentFileList()
{
   ItFileSetHt = SingleSerieUIDFileSetHT.begin();
   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
      return ItFileSetHt->second;
   return NULL;
}

/**
 * \brief   accessor (DEPRECATED :  use GetNextSingleSerieUIDFileSet )
 *          Warning : 'coherent' means here they have the same Serie UID
 * \note : meaningfull only if GetFirstCoherentFileList() already called 
 * @return  The next FileList if found, otherwhise NULL
 */
FileList *SerieHelper::GetNextCoherentFileList()
{
   gdcmAssertMacro (ItFileSetHt != SingleSerieUIDFileSetHT.end());
  
   ++ItFileSetHt;
   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
      return ItFileSetHt->second;
   return NULL;
}

/**
 * \brief   accessor (DEPRECATED :  use GetSingleSerieUIDFileSet )
  *          Warning : 'coherent' means here they have the same Serie UID
 * @param SerieUID SerieUID
 * \return  pointer to the FileList if found, otherwhise NULL
 */
FileList *SerieHelper::GetCoherentFileList(std::string SerieUID)
{
   if ( SingleSerieUIDFileSetHT.count(SerieUID) == 0 )
      return 0;     
   return SingleSerieUIDFileSetHT[SerieUID];
}
#endif


/**
 * \brief   Get the first Fileset while visiting the SingleSerieUIDFileSetmap
 * @return  The first FileList (SingleSerieUIDFileSet) if found, otherwhise 0
 */
FileList *SerieHelper::GetFirstSingleSerieUIDFileSet()
{
   ItFileSetHt = SingleSerieUIDFileSetHT.begin();
   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
      return ItFileSetHt->second;
   return 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::GetNextSingleSerieUIDFileSet()
{
   gdcmAssertMacro (ItFileSetHt != SingleSerieUIDFileSetHT.end());
  
   ++ItFileSetHt;
   if ( ItFileSetHt != SingleSerieUIDFileSetHT.end() )
      return ItFileSetHt->second;
   return 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::GetSingleSerieUIDFileSet(std::string SerieUID)
{
   if ( SingleSerieUIDFileSetHT.count(SerieUID) == 0 )
      return 0;     
   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;

   size_t nb = fileSet->size();
   if (nb == 0 )
      return CoherentFileSet;
   float iop[6];
   std::string strOrient;
   itksys_ios::ostringstream ossOrient;

   FileList::const_iterator it = fileSet->begin();
   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("");
      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;

   size_t nb = fileSet->size();
   if (nb == 0 )
      return CoherentFileSet;
   float pos[3];
   std::string strImPos;  // read on disc
   itksys_ios::ostringstream ossPosition;
   std::string strPosition; // re computed
   FileList::const_iterator it = fileSet->begin();
   for ( ;
         it != fileSet->end();
       ++it)
   {     
      // Information is in :      
      // 0020,0032 : Image Position Patient
      // 0020,0030 : Image Position (RET)

      strImPos = (*it)->GetEntryValue(0x0020,0x0032);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -