00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
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:
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 , true );
00093 break;
00094 case FILEFORMAT_ANALYZE_HDR_BIGENDIAN:
00095 volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, true , true );
00096 break;
00097 case FILEFORMAT_NIFTI_SINGLEFILE:
00098 volume = VolumeFromFile::ReadNifti( translatedPath, false , true );
00099 break;
00100 case FILEFORMAT_NIFTI_DETACHED:
00101 volume = VolumeFromFile::ReadNifti( translatedPath, true , true );
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
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 , false );
00179 break;
00180 case FILEFORMAT_ANALYZE_HDR_BIGENDIAN:
00181 volume = VolumeFromFile::ReadAnalyzeHdr( translatedPath, true , false );
00182 break;
00183 case FILEFORMAT_NIFTI_SINGLEFILE:
00184 volume = VolumeFromFile::ReadNifti( translatedPath, false , false );
00185 break;
00186 case FILEFORMAT_NIFTI_DETACHED:
00187 volume = VolumeFromFile::ReadNifti( translatedPath, true , false );
00188 break;
00189 default:
00190 {
00191
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
00281 if ( ! strcmp( suffix, ".gz" ) )
00282 {
00283
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
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 }