cmtkVolumeIO.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 1997-2009 Torsten Rohlfing
00004 //
00005 //  Copyright 2004-2010 SRI International
00006 //
00007 //  This file is part of the Computational Morphometry Toolkit.
00008 //
00009 //  http://www.nitrc.org/projects/cmtk/
00010 //
00011 //  The Computational Morphometry Toolkit is free software: you can
00012 //  redistribute it and/or modify it under the terms of the GNU General Public
00013 //  License as published by the Free Software Foundation, either version 3 of
00014 //  the License, or (at your option) any later version.
00015 //
00016 //  The Computational Morphometry Toolkit is distributed in the hope that it
00017 //  will be useful, but WITHOUT ANY WARRANTY; without even the implied
00018 //  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 //  GNU General Public License for more details.
00020 //
00021 //  You should have received a copy of the GNU General Public License along
00022 //  with the Computational Morphometry Toolkit.  If not, see
00023 //  <http://www.gnu.org/licenses/>.
00024 //
00025 //  $Revision: 2591 $
00026 //
00027 //  $LastChangedDate: 2010-12-03 13:47:07 -0800 (Fri, 03 Dec 2010) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include "cmtkVolumeIO.h"
00034 
00035 #include <System/cmtkStrUtility.h>
00036 #include <System/cmtkFileUtils.h>
00037 #include <System/cmtkProgress.h>
00038 #include <System/cmtkMountPoints.h>
00039 
00040 #include <IO/cmtkStudy.h>
00041 #include <IO/cmtkClassStream.h>
00042 #include <IO/cmtkVolumeFromFile.h>
00043 #include <IO/cmtkPGM.h>
00044 
00045 #include <Base/cmtkTypes.h>
00046 
00047 #ifdef HAVE_UNISTD_H
00048 #  include <unistd.h>
00049 #endif
00050 
00051 #ifdef HAVE_LIBGEN_H
00052 #  include <libgen.h>
00053 #endif
00054 
00055 #include <limits.h>
00056 #include <math.h>
00057 #include <stdio.h>
00058 
00059 namespace
00060 cmtk
00061 {
00062 
00065 
00067 const char* const CMTK_LEGACY_WRITE_IMAGES_RAS = "CMTK_LEGACY_WRITE_IMAGES_RAS";
00068 
00070 bool VolumeIO::WriteCompressedOn = true;
00071 
00072 UniformVolume::SmartPtr
00073 VolumeIO::Read( const char* path, const bool verbose )
00074 {
00075   UniformVolume::SmartPtr volume( NULL );
00076 
00077   const char *translatedPath = MountPoints::Translate( path );
00078 
00079   FileFormatID formatID = FileFormat::Identify( translatedPath );
00080   switch ( formatID ) 
00081     {
00082     case FILEFORMAT_DICOM: // (hopefully) multi-slice DICOM
00083       volume = VolumeFromFile::ReadDICOM( translatedPath );
00084       break;
00085     case FILEFORMAT_VANDERBILT:
00086       volume = VolumeFromFile::ReadVanderbilt( translatedPath );
00087       break;
00088     case FILEFORMAT_BIORAD:
00089       volume = VolumeFromFile::ReadBioRad( translatedPath );
00090       break;
00091     case FILEFORMAT_ANALYZE_HDR:
00092       volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, false /*bigendian*/, true /*readData*/ );
00093       break;
00094     case FILEFORMAT_ANALYZE_HDR_BIGENDIAN:
00095       volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, true /*bigendian*/, true /*readData*/ );
00096       break;
00097     case FILEFORMAT_NIFTI_SINGLEFILE:
00098       volume = VolumeFromFile::ReadNifti( translatedPath, false /*detached*/, true /*readData*/ );
00099       break;
00100     case FILEFORMAT_NIFTI_DETACHED:
00101       volume = VolumeFromFile::ReadNifti( translatedPath, true /*detached*/, true /*readData*/ );
00102       break;
00103     case FILEFORMAT_NRRD:
00104       volume = VolumeFromFile::ReadNRRD( translatedPath );
00105       break;
00106     default: 
00107     {
00108     }
00109     }
00110   
00111   if ( volume ) 
00112     {
00113     volume->m_MetaInformation[META_FILEFORMAT_ORIGINAL] = FileFormat::Describe( formatID );
00114     // for float and double data, automatically recognize Inf as Null Data.
00115     TypedArray::SmartPtr dataArray = volume->GetData();
00116     if ( dataArray ) 
00117       {
00118       if ( dataArray->GetType() == TYPE_FLOAT )
00119         {
00120         const float fInf = MathUtil::GetFloatInf();
00121         dataArray->SetPaddingPtr( &fInf );
00122         }
00123       else
00124         {
00125         if ( dataArray->GetType() == TYPE_DOUBLE )
00126           {
00127           const double dInf = MathUtil::GetDoubleInf();
00128           dataArray->SetPaddingPtr( &dInf );
00129           }
00130         }
00131 
00132       for ( size_t i = 0; i < dataArray->GetDataSize(); ++i )
00133         {
00134         Types::DataItem v;
00135         if ( dataArray->Get( v, i ) && MathUtil::IsNaN( v ) )
00136           dataArray->SetPaddingAt( i );
00137         }
00138       }
00139     }
00140   
00141   if ( verbose && volume ) 
00142     {
00143     StdErr.printf( "%s\nRead %d x %d x %d voxels [%f x %f x %f mm total size].\n", path,
00144                       volume->GetDims()[0], volume->GetDims()[1], volume->GetDims()[2],
00145                       volume->Size[0], volume->Size[1], volume->Size[2] );
00146     
00147     const TypedArray* dataArray = volume->GetData();
00148     if ( dataArray ) 
00149       {
00150       const Types::DataItemRange range = dataArray->GetRange();
00151       StdErr.printf( "Data type %s, range [%f .. %f]\n", DataTypeName[ dataArray->GetType() ],
00152                      static_cast<float>( range.m_LowerBound ), static_cast<float>( range.m_UpperBound ) );
00153       } 
00154     else
00155       {
00156       StdErr << "Image does not contain valid data.\n";
00157       }
00158     }
00159 
00160   if ( volume )
00161     {
00162     volume->m_MetaInformation[META_FS_PATH] = path;
00163     }
00164   
00165   return volume;
00166 }
00167 
00168 UniformVolume::SmartPtr
00169 VolumeIO::ReadGrid( const char* path, const bool verbose )
00170 {
00171   UniformVolume::SmartPtr volume( NULL );
00172 
00173   const char *translatedPath = MountPoints::Translate( path );
00174 
00175   switch ( FileFormat::Identify( translatedPath ) ) 
00176     {
00177     case FILEFORMAT_ANALYZE_HDR:
00178       volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, false /*bigendian*/, false /*readData*/ );
00179       break;
00180     case FILEFORMAT_ANALYZE_HDR_BIGENDIAN:
00181       volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, true /*bigendian*/, false /*readData*/ );
00182       break;
00183     case FILEFORMAT_NIFTI_SINGLEFILE:
00184       volume = VolumeFromFile::ReadNifti( translatedPath, false /*detached*/, false /*readData*/ );
00185       break;
00186     case FILEFORMAT_NIFTI_DETACHED:
00187       volume = VolumeFromFile::ReadNifti( translatedPath, true /*detached*/, false /*readData*/ );
00188       break;
00189     default: 
00190     {
00191     // For now, default to full reader for other image file formats
00192     volume = VolumeIO::Read( path, verbose );
00193     }
00194     }
00195   
00196   if ( verbose && volume ) 
00197     {
00198     StdErr.printf( "%s\nRead %d x %d x %d voxels [%f x %f x %f mm total size].\n", path,
00199                       volume->GetDims()[0], volume->GetDims()[1], volume->GetDims()[2],
00200                       volume->Size[0], volume->Size[1], volume->Size[2] );
00201     }
00202   
00203   if ( volume )
00204     {
00205     volume->m_MetaInformation[META_FS_PATH] = path;
00206     }
00207   
00208   return volume;
00209 }
00210 
00211 UniformVolume::SmartPtr
00212 VolumeIO
00213 ::ReadGridOriented( const char *path, const char* orientation, const bool verbose )
00214 {
00215   UniformVolume::SmartPtr volume( Self::ReadGrid( path, verbose ) );
00216   if ( !volume ) 
00217     return volume;
00218   
00219   const std::string orientationOriginal = volume->m_MetaInformation[META_IMAGE_ORIENTATION];
00220   if ( orientationOriginal == "" )
00221     {
00222     StdErr << "WARNING: image does not have valid orientation meta information; cannot reorient.\n";
00223     return volume;
00224     }
00225   else
00226     {
00227     if ( orientationOriginal != orientation )
00228       {
00229       if ( verbose )
00230         {
00231         StdErr << "INFO: reorienting image from '" << orientationOriginal << "' to '" << orientation << "'\n";
00232         }
00233       
00234       return volume->GetReoriented( orientation );
00235       }
00236     }
00237 
00238   return volume;
00239 }
00240 
00241 UniformVolume::SmartPtr
00242 VolumeIO
00243 ::ReadOriented( const char *path, const char* orientation, const bool verbose )
00244 {
00245   UniformVolume::SmartPtr volume( VolumeIO::Read( path, verbose ) );
00246   if ( !volume ) 
00247     return volume;
00248 
00249   const std::string orientationOriginal = volume->m_MetaInformation[META_IMAGE_ORIENTATION];
00250   if ( orientationOriginal == "" )
00251     {
00252     StdErr << "WARNING: image does not have valid orientation meta information; cannot reorient.\n";
00253     return volume;
00254     }
00255   else
00256     {
00257     if ( orientationOriginal != orientation )
00258       {
00259       if ( verbose )
00260         {
00261         StdErr << "INFO: reorienting image from '" << orientationOriginal << "' to '" << orientation << "'\n";
00262         }
00263       
00264       return volume->GetReoriented( orientation );
00265       }
00266     }
00267   return volume;
00268 }
00269 
00270 void 
00271 VolumeIO::Write
00272 ( const UniformVolume& volume, const char *pathAndFormat, const bool verbose )
00273 {
00274   const char* actualPath = pathAndFormat;
00275   FileFormatID fileFormat = FILEFORMAT_UNKNOWN;
00276 
00277   const char* suffix = strrchr( pathAndFormat, '.' );  
00278   if ( suffix )
00279     {
00280     // check whether we have a compression-related suffix
00281     if ( ! strcmp( suffix, ".gz" ) )
00282       {
00283       // include actual suffix
00284       while ( suffix != pathAndFormat )
00285         {
00286         --suffix;
00287         if ( *suffix == '.' )
00288           break;
00289         }
00290       }
00291 
00292     if ( ! strcmp( ".hdr", suffix ) )
00293       {
00294       fileFormat = FILEFORMAT_ANALYZE_HDR;
00295       }
00296     else
00297       {
00298       if ( ! strcmp( ".img", suffix ) || ! strcmp( ".img.gz", suffix ) )
00299         {
00300         fileFormat = FILEFORMAT_NIFTI_DETACHED;
00301         }
00302       else
00303         {
00304         if ( ! strcmp( ".nii", suffix ) || ! strcmp( ".nii.gz", suffix ) )
00305           {
00306           fileFormat = FILEFORMAT_NIFTI_SINGLEFILE;
00307           }
00308         else
00309           {
00310           if ( ! strcmp( ".mha", suffix ) )
00311             {
00312             fileFormat = FILEFORMAT_METAIMAGE;
00313             }
00314           else 
00315             {
00316             if ( ! strcmp( ".nrrd", suffix ) || ! strcmp( ".nhdr", suffix ) )
00317               {
00318               fileFormat = FILEFORMAT_NRRD;
00319               }
00320             }
00321           }
00322         }
00323       }
00324     }
00325  
00326 #ifndef _MSC_VER
00327   const char* colon = strchr( pathAndFormat, ':' );
00328   if ( colon != NULL ) 
00329     {
00330     actualPath = colon+1;
00331     unsigned int formatLength = colon - pathAndFormat - 1;
00332     
00333     if ( ! strncmp( "ANALYZE", pathAndFormat, formatLength ) ) 
00334       {
00335       fileFormat = FILEFORMAT_ANALYZE_HDR;
00336       } 
00337     else if ( ! strncmp( "NIFTI", pathAndFormat, formatLength ) ) 
00338       {
00339       fileFormat = FILEFORMAT_NIFTI_SINGLEFILE;
00340       } 
00341     else if ( ! strncmp( "NRRD", pathAndFormat, formatLength ) ) 
00342       {
00343       fileFormat = FILEFORMAT_NRRD;
00344       } 
00345     else if ( ! strncmp( "METAIMAGE", pathAndFormat, formatLength ) ) 
00346       {
00347       fileFormat = FILEFORMAT_METAIMAGE;
00348       } 
00349     }
00350 #endif
00351 
00352   if ( fileFormat == FILEFORMAT_UNKNOWN )
00353     {
00354     StdErr << "Fileformat not recognized; writing single-file NIFTI instead.\n";
00355     fileFormat = FILEFORMAT_NIFTI_SINGLEFILE;
00356     }
00357   
00358   char absolutePath[PATH_MAX];
00359   FileUtils::GetAbsolutePath( absolutePath, actualPath );
00360   
00361   Write( volume, fileFormat, absolutePath, verbose );
00362 }
00363 
00364 void
00365 VolumeIO::Write
00366 ( const UniformVolume& volume, const FileFormatID format, const char* path, const bool verbose )
00367 {
00368   const TypedArray *data = volume.GetData();
00369   if ( data == NULL ) return;
00370 
00371   FileUtils::RecursiveMkPrefixDir( path );
00372 
00373   const UniformVolume* actualVolume = &volume;
00374 
00375   // if volume was reoriented from its original array order, temporarily reorient back and set actual volume to temporary volume.
00376   cmtk::UniformVolume::SmartConstPtr reorientedVolume;
00377 
00378   if ( !getenv( CMTK_LEGACY_WRITE_IMAGES_RAS ) )
00379     {
00380     if ( volume.MetaKeyExists( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ) &&
00381          (volume.GetMetaInfo( cmtk::META_IMAGE_ORIENTATION ) != volume.GetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ) ) )
00382       {
00383       reorientedVolume = cmtk::UniformVolume::SmartConstPtr( volume.GetReoriented( volume.GetMetaInfo( cmtk::META_IMAGE_ORIENTATION_ORIGINAL ).c_str() ) );
00384       actualVolume = reorientedVolume;
00385       }
00386     }
00387     
00388   switch ( format ) 
00389     {
00390     case FILEFORMAT_ANALYZE_HDR: 
00391     {
00392     VolumeFromFile::WriteAnalyzeHdr( path, *actualVolume, verbose );
00393     break;
00394     }
00395     case FILEFORMAT_NIFTI_DETACHED: 
00396     case FILEFORMAT_NIFTI_SINGLEFILE: 
00397     {
00398     VolumeFromFile::WriteNifti( path, *actualVolume, verbose );
00399     break;
00400     }
00401     case FILEFORMAT_METAIMAGE: 
00402     {
00403     VolumeFromFile::WriteMetaImage( path, *actualVolume );
00404     break;
00405     }
00406     case FILEFORMAT_NRRD: 
00407     {
00408     VolumeFromFile::WriteNRRD( path, *actualVolume, verbose );
00409     break;
00410     }
00411     break;
00412     default:
00413       break;
00414     }
00415   
00416   volume.m_MetaInformation[META_FS_PATH] = path;
00417 }
00418 
00419 VolumeIO::Initializer::Initializer()
00420 {
00421   if ( getenv( "IGS_WRITE_UNCOMPRESSED" ) || getenv( "CMTK_WRITE_UNCOMPRESSED" ) )
00422     VolumeIO::SetWriteCompressedOff();
00423 }
00424 
00425 VolumeIO::Initializer VolumeIO::Initializer::Instance;
00426 
00427 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines