cmtkDICOM.cxx

Go to the documentation of this file.
00001 /*
00002 //
00003 //  Copyright 2004-2010 SRI International
00004 //
00005 //  Copyright 1997-2009 Torsten Rohlfing
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: 2398 $
00026 //
00027 //  $LastChangedDate: 2010-10-05 14:54:37 -0700 (Tue, 05 Oct 2010) $
00028 //
00029 //  $LastChangedBy: torstenrohlfing $
00030 //
00031 */
00032 
00033 #include "cmtkDICOM.h"
00034 
00035 #include <Base/cmtkTypedArray.h>
00036 
00037 #include <System/cmtkConsole.h>
00038 
00039 #include <dcmtk/dcmdata/dcdeftag.h>
00040 #include <dcmtk/dcmimgle/didocu.h>
00041 #include <dcmtk/dcmimgle/diutils.h>
00042 
00043 #ifdef CMTK_HAVE_DCMTK_JPEG
00044 #  include <djdecode.h>
00045 #endif
00046 
00047 #ifdef HAVE_SYS_TYPES_H
00048 #  include <sys/types.h>
00049 #endif
00050 
00051 #include <memory>
00052 #include <string.h>
00053 #include <stdio.h>
00054 #include <ctime>
00055 
00056 namespace
00057 cmtk
00058 {
00059 
00062 
00063 ScalarImage* 
00064 DICOM::Read 
00065 ( const char *path )
00066 {
00067   ScalarImage* image = NULL;
00068 
00069 #ifdef CMTK_HAVE_DCMTK_JPEG
00070   // register global decompression codecs
00071   static bool decodersRegistered = false;
00072   if ( ! decodersRegistered ) 
00073     {
00074     DJDecoderRegistration::registerCodecs( EDC_photometricInterpretation, EUC_default, EPC_default, 1 );
00075     decodersRegistered = true;
00076     }
00077 #endif
00078   
00079   std::auto_ptr<DcmFileFormat> fileformat( new DcmFileFormat );
00080   if (!fileformat.get()) 
00081     {
00082     StdErr << "ERROR: Could not create DICOM file format object.\n";
00083     return NULL;
00084     }
00085   
00086   fileformat->transferInit();
00087   OFCondition status = fileformat->loadFile( path );
00088   fileformat->transferEnd();
00089 
00090   if ( !status.good() ) 
00091     {
00092     StdErr << "Error: cannot read DICOM file " << path << " (" << status.text() << ")\n";
00093     throw (0);
00094     }
00095   
00096   DcmDataset *dataset = fileformat->getAndRemoveDataset();
00097 
00098   if ( !dataset ) 
00099     {
00100     StdErr << "ERROR: File format has NULL dataset.\n";
00101     return NULL;
00102     }
00103   
00104   std::auto_ptr<DiDocument> document( new DiDocument( dataset, dataset->getOriginalXfer(), CIF_AcrNemaCompatibility ) );
00105   if ( ! document.get() || ! document->good() ) 
00106     {
00107     StdErr << "ERROR: Could not create document representation.\n";
00108     return NULL;
00109     }
00110   
00111   Uint16 dimsX, dimsY;
00112   if ( ! ( document->getValue( DCM_Columns, dimsX ) && document->getValue( DCM_Rows, dimsY ) ) ) 
00113     {
00114     StdErr << "ERROR: File " << path << " has no DCM_Columns/DCM_Rows tags.\n";
00115     return NULL; // no image dimensions, nothing we can do.
00116     }
00117   
00118   Uint16 numberOfFrames = 1;
00119   if ( ! document->getValue( DCM_NumberOfFrames, numberOfFrames ) )
00120     numberOfFrames = 1;
00121   image = new ScalarImage( dimsX, dimsY, numberOfFrames );
00122   
00123   // get calibration from image
00124   double calibrationX = -1, calibrationY = -1;
00125   bool hasPixelSpacing = (document->getValue(DCM_PixelSpacing, calibrationX, 0) > 0);
00126   if ( hasPixelSpacing ) 
00127     {
00128     if ( document->getValue(DCM_PixelSpacing, calibrationY, 1) < 2)
00129       calibrationY = calibrationX;
00130     } 
00131   else
00132     calibrationX = calibrationY = -1;
00133   image->SetPixelSize( calibrationX, calibrationY );
00134   
00135   Uint16 bitsAllocated = 0;
00136   if ( ! document->getValue( DCM_BitsAllocated, bitsAllocated ) )
00137     // No "BitsAllocated" tag; use "BitsStored" instead.
00138     document->getValue( DCM_BitsStored, bitsAllocated );
00139   
00140   bool pixelDataSigned = false;
00141   Uint16 pixelRepresentation = 0;
00142   if ( document->getValue( DCM_PixelRepresentation, pixelRepresentation ) > 0)
00143     pixelDataSigned = (pixelRepresentation == 1);
00144   
00145   unsigned long totalImageSizePixels = image->GetDims()[AXIS_X] * image->GetDims()[AXIS_Y] * image->GetNumberOfFrames();
00146 
00147   double rescaleIntercept, rescaleSlope;
00148   const bool haveRescaleIntercept = (0 != document->getValue( DCM_RescaleIntercept, rescaleIntercept ));
00149   if ( ! haveRescaleIntercept )
00150     rescaleIntercept = 0;
00151 
00152   const bool haveRescaleSlope = (0 != document->getValue( DCM_RescaleSlope, rescaleSlope ));
00153   if ( ! haveRescaleSlope )
00154     rescaleSlope = 1;
00155   
00156   pixelDataSigned = pixelDataSigned || (rescaleIntercept < 0);
00157   
00158   TypedArray::SmartPtr pixelDataArray;
00159 
00160 #ifdef DCM_VariablePixelData
00161   DcmElement *delem = document->search( DCM_VariablePixelData );
00162 #else
00163   DcmElement *delem = document->search( DCM_ACR_NEMA_2C_VariablePixelData );
00164 #endif
00165   if (!delem)
00166     delem = document->search( DCM_PixelData );
00167     
00168   if (delem) 
00169     {
00170     if ( (delem->getTag().getEVR() == EVR_OW) || (bitsAllocated > 8) ) 
00171       {
00172       Uint16 *pdata = NULL;
00173       delem->getUint16Array(pdata);
00174       if ( pixelDataSigned ) 
00175         {
00176         pixelDataArray = TypedArray::Create( TYPE_SHORT, pdata, totalImageSizePixels );
00177         } 
00178       else
00179         {
00180         pixelDataArray = TypedArray::Create( TYPE_USHORT, pdata, totalImageSizePixels );
00181         }
00182       } 
00183     else
00184       {
00185       Uint8 *pdata = NULL;
00186       delem->getUint8Array(pdata);
00187       if ( pixelDataSigned ) 
00188         {
00189         pixelDataArray = TypedArray::Create( TYPE_CHAR, pdata, totalImageSizePixels );
00190         } 
00191       else 
00192         {
00193         pixelDataArray = TypedArray::Create( TYPE_BYTE, pdata, totalImageSizePixels );
00194         }
00195       }
00196     delem->detachValueField();
00197     }
00198   
00199   if ( ! pixelDataArray ) 
00200     {
00201     StdErr.printf( "Could not read pixel data from image file\n%s", path );
00202     }
00203   
00204   Uint16 paddingValue = 0;
00205   if ( ( dataset->findAndGetUint16( DCM_PixelPaddingValue, paddingValue )).good() )
00206     pixelDataArray->SetPaddingValue( paddingValue );
00207   
00208   if ( haveRescaleIntercept || haveRescaleSlope )
00209     pixelDataArray->Rescale( rescaleSlope, rescaleIntercept );
00210 
00211   image->SetPixelData( TypedArray::SmartPtr( pixelDataArray ) );
00212 
00213   // now some more manual readings...
00214 
00215     // get slice spacing from multi-slice images.
00216   double frameToFrame = 0;
00217   if ( document->getValue( DCM_SpacingBetweenSlices, frameToFrame ) )
00218     image->SetFrameToFrameSpacing( frameToFrame );
00219 
00220     // get original table position from image.
00221   double sliceLocation = 0;
00222   if ( ! document->getValue( DCM_SliceLocation, sliceLocation ) ) 
00223     {
00224 #ifdef DCM_Location
00225     document->getValue( DCM_Location, sliceLocation );
00226 #else
00227     document->getValue( DCM_ACR_NEMA_Location, sliceLocation );
00228 #endif
00229     }
00230   image->SetImageSlicePosition( sliceLocation );
00231   
00232   // Use table position to set image position as long as we don't know
00233   // better.
00234   ScalarImage::SpaceVectorType imageOrigin( ScalarImage::SpaceVectorType::Init( 0 ) );
00235   imageOrigin[2] = sliceLocation;
00236       
00237   // get original image position from file.
00238   const char *image_position_s = NULL;
00239   if ( ! document->getValue( DCM_ImagePositionPatient, image_position_s ) ) 
00240     {
00241     // ImagePositionPatient tag not present, try ImagePosition instead
00242 #ifdef DCM_ImagePosition
00243     document->getValue( DCM_ImagePosition, image_position_s );
00244 #else
00245     document->getValue( DCM_ACR_NEMA_ImagePosition, image_position_s );
00246 #endif
00247     }
00248   if ( image_position_s ) 
00249     {
00250     double xyz[3];
00251     if ( 3 == sscanf( image_position_s,"%lf%*c%lf%*c%lf", xyz, xyz+1, xyz+2 ) ) 
00252       {
00253       imageOrigin = ScalarImage::SpaceVectorType( xyz );
00254       }
00255     }
00256   
00257   image->SetImageOrigin( imageOrigin );
00258   
00259   // get original image direction from file.
00260   ScalarImage::SpaceVectorType imageDirectionX( ScalarImage::SpaceVectorType::Init(0) );
00261   imageDirectionX[0] = 1;
00262   ScalarImage::SpaceVectorType imageDirectionY( ScalarImage::SpaceVectorType::Init(0) );
00263   imageDirectionY[1] = 1;
00264 
00265   const char *image_orientation_s = NULL;
00266   document->getValue( DCM_ImageOrientationPatient, image_orientation_s );
00267   if ( image_orientation_s ) 
00268     {
00269     double dx[3], dy[3];
00270     if ( 6 == sscanf( image_orientation_s, "%lf%*c%lf%*c%lf%*c%lf%*c%lf%*c%lf", dx, dx+1, dx+2, dy, dy+1, dy+2 ) ) 
00271       {
00272       imageDirectionX = ScalarImage::SpaceVectorType( dx );
00273       imageDirectionY = ScalarImage::SpaceVectorType( dy );
00274       }
00275     }
00276   
00277   image->SetImageDirectionX( imageDirectionX );
00278   image->SetImageDirectionY( imageDirectionY );
00279 
00280   return image;
00281 }
00282 
00284 
00285 } // namespace cmtk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines