vtkgdcmSerieViewer2.cxx

Go to the documentation of this file.
00001 /*=========================================================================
00002                                                                                 
00003   Program:   gdcm
00004   Module:    $RCSfile: vtkgdcmSerieViewer2.cxx,v $
00005   Language:  C++
00006   Date:      $Date: 2007/06/21 14:47:16 $
00007   Version:   $Revision: 1.11 $
00008                                                                                 
00009   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
00010   l'Image). All rights reserved. See Doc/License.txt or
00011   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
00012                                                                                 
00013      This software is distributed WITHOUT ANY WARRANTY; without even
00014      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
00015      PURPOSE.  See the above copyright notices for more information.
00016                                                                                 
00017 =========================================================================*/
00018 // This example illustrates how the vtkGdcmReader vtk class can 
00019 // use the result of GDCM_NAME_SPACE::SerieHelper constructor and check
00020 // the various Setters :
00021 //     SerieHelper::SetOrderToReverse, 
00022 //     SerieHelper::SetUserLessThanFunction
00023 //     SerieHelper::SetLoadMode
00024 //     SerieHelper::SetDropDuplicatePositions
00025 //     vtkGdcmReader::SetUserFunction
00026 //     vtkGdcmReader::SetCoherentFileList
00027 // Usage:
00028 //  * the Directory name that contains the Dicom images constituting the stack 
00029 //    should be given as command line argument (keyword : dirname=),
00030 //  * you can navigate through the stack by hitting any character key,
00031 //  * the produced vtk file is named "foo.vtk" (in the invocation directory).
00032 // 
00033 //----------------------------------------------------------------------------
00034 #include <vtkRenderWindowInteractor.h>
00035 #include <vtkImageViewer2.h>
00036 #include <vtkStructuredPoints.h>
00037 #include <vtkStructuredPointsWriter.h>
00038 #include <vtkCommand.h>
00039 #include <vtkRenderer.h>
00040 #include <vtkImageMapToColors.h>
00041 #include <vtkLookupTable.h>
00042 
00043 #include "vtkGdcmReader.h"
00044 #include "gdcmDocument.h"  // for NO_SHADOWSEQ
00045 #include "gdcmSerieHelper.h"
00046 #include "gdcmDebug.h"
00047 #include "gdcmDataEntry.h"
00048 
00049 #include "gdcmArgMgr.h" // for Argument Manager functions
00050 #include <string.h>     // for strcmp
00051 #ifndef vtkFloatingPointType
00052 #define vtkFloatingPointType float
00053 #endif
00054 
00055 void userSuppliedMirrorFunction (uint8_t *im, GDCM_NAME_SPACE::File *f);
00056 void userSuppliedUpsideDownFunction(uint8_t *im, GDCM_NAME_SPACE::File *f);
00057 bool userSuppliedLessThanFunction(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2);
00058 bool userSuppliedLessThanFunction2(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2);
00059 
00060 int orderNb;
00061 uint16_t *elemsToOrderOn;
00062 
00063 //----------------------------------------------------------------------------
00064 // Callback for the interaction
00065 class vtkgdcmObserver : public vtkCommand
00066 {
00067 public:
00068    virtual char const *GetClassName() const 
00069    { 
00070       return "vtkgdcmObserver";
00071    }
00072 
00073    static vtkgdcmObserver *New() 
00074    { 
00075       return new vtkgdcmObserver; 
00076    }
00077 
00078    vtkgdcmObserver()
00079    {
00080       this->ImageViewer = NULL;
00081    }
00082 
00083    virtual void Execute(vtkObject *, unsigned long event, void* )
00084    {
00085       if ( this->ImageViewer )
00086       {
00087          if ( event == vtkCommand::CharEvent )
00088          {
00089 #if (VTK_MAJOR_VERSION >= 5)
00090             int max = ImageViewer->GetSliceMax();
00091             int slice = (ImageViewer->GetSlice() + 1 ) % ++max;
00092             ImageViewer->SetSlice( slice );
00093 #else
00094             int max = ImageViewer->GetWholeZMax();
00095             int slice = (ImageViewer->GetZSlice() + 1 ) % ++max;
00096             ImageViewer->SetZSlice( slice );
00097 #endif
00098 #if !( (VTK_MAJOR_VERSION >= 5) || ( VTK_MAJOR_VERSION == 4 && VTK_MINOR_VERSION >= 5 ) )
00099          // This used to be a bug in version VTK 4.4 and earlier
00100             ImageViewer->GetRenderer()->ResetCameraClippingRange();
00101 #endif
00102             ImageViewer->Render();
00103          }
00104       }
00105    }
00106    vtkImageViewer2 *ImageViewer;
00107 };
00108 
00109 int main(int argc, char *argv[])
00110 {
00111    START_USAGE(usage)
00112    " \n vtkgdcmSerieViewer2 : \n",
00113    " Display a 'Serie' (same Serie UID) within a Directory                    ",
00114    " You can navigate through the stack by hitting any character key.         ",
00115    " usage: vtkgdcmSerieViewer dirname=sourcedirectory                        ",
00116    "                           [noshadowseq][noshadow][noseq]                 ",
00117    "                           [reverse] [{[mirror]|[topdown]|[rotate]}]      ",
00118    "                           [order=] [nodup][check][debug]                 ",
00119    "      sourcedirectory : name of the directory holding the images          ",
00120    "                        if it holds more than one serie,                  ",
00121    "                        only the first one is displayed.                  ",
00122    "      noshadowseq: user doesn't want to load Private Sequences            ",
00123    "      noshadow   : user doesn't want to load Private groups (odd number)  ",
00124    "      noseq      : user doesn't want to load Sequences                    ",
00125    "      reverse    : user wants to sort the images reverse order            ",
00126    "      mirror     : user wants to 'mirror' the images    | just some simple",
00127    "      upsidedown : user wants to 'upsidedown' the images| examples of user",
00128    "                                                        | suppliedfunction",
00129    "      check      : user wants to force more coherence checking            ",
00130    "      order=     : group1-elem1,group2-elem2,... (in hexa, no space)      ",
00131    "                   if we want to use them as a sort criterium             ",
00132    "                   Right now : ValEntries only -just an example-          ",
00133    "        or                                                                ",
00134    "      order=     : order=name if we want to sort on file name (why not ?) ",
00135    "      nodup       : user wants to drop duplicate positions                ",
00136    "      debug      : developper wants to run the program in 'debug mode'    ",
00137    FINISH_USAGE
00138 
00139 
00140    // Initialize Arguments Manager   
00141    GDCM_NAME_SPACE::ArgMgr *am= new GDCM_NAME_SPACE::ArgMgr(argc, argv);
00142   
00143    if (argc == 1 || am->ArgMgrDefined("usage") )
00144    {
00145       am->ArgMgrUsage(usage); // Display 'usage'
00146       delete am;
00147       return 0;
00148    }
00149 
00150    char *dirName = am->ArgMgrWantString("dirname",usage);
00151 
00152    int loadMode = GDCM_NAME_SPACE::LD_ALL;
00153    if ( am->ArgMgrDefined("noshadowseq") )
00154       loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ;
00155    else 
00156    {
00157       if ( am->ArgMgrDefined("noshadow") )
00158          loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW;
00159       if ( am->ArgMgrDefined("noseq") )
00160          loadMode |= GDCM_NAME_SPACE::LD_NOSEQ;
00161    }
00162 
00163    int reverse = am->ArgMgrDefined("reverse");
00164    int nodup   = am->ArgMgrDefined("nodup");
00165    int mirror     = am->ArgMgrDefined("mirror");
00166    int upsidedown = am->ArgMgrDefined("upsidedown");
00167 
00168    if ( mirror && upsidedown )
00169    {
00170       std::cout << "*EITHER* mirror *OR* upsidedown !"
00171                 << std::endl;
00172       delete am;
00173       return 0;
00174    }
00175 
00176    int check   = am->ArgMgrDefined("check");
00177   
00178    // This is so ugly, a cstring is NOT a char * (god damit!)
00179    bool bname = ( strcmp(am->ArgMgrGetString("order", "not found"),"name")==0 );
00180    if (bname)
00181       elemsToOrderOn = am->ArgMgrGetXInt16Enum("order", &orderNb);
00182 
00183    if (am->ArgMgrDefined("debug"))
00184       GDCM_NAME_SPACE::Debug::DebugOn();
00185 
00186    /* if unused Param we give up */
00187    if ( am->ArgMgrPrintUnusedLabels() )
00188    {
00189       am->ArgMgrUsage(usage);
00190       delete am;
00191       return 0;
00192    } 
00193 
00194    delete am;  // we don't need Argument Manager any longer
00195 
00196    // ----------------------- End Arguments Manager ----------------------
00197   
00198    GDCM_NAME_SPACE::SerieHelper *sh = GDCM_NAME_SPACE::SerieHelper::New();
00199    sh->SetLoadMode(loadMode);
00200    if (reverse)
00201       sh->SetSortOrderToReverse();
00202    sh->SetDirectory( dirName, true);
00203     
00204    // Just to see
00205 
00206    int nbFiles;
00207    // For all the 'Single Serie UID' FileSets of the GDCM_NAME_SPACE::Serie
00208    GDCM_NAME_SPACE::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
00209    if (l == 0 )
00210    {
00211       std::cout << "Oops! No 'Single Serie UID' FileSet found ?!?" << std::endl;
00212       return 0;
00213    }
00214 
00215    if (bname)
00216      sh->SetUserLessThanFunction(userSuppliedLessThanFunction2);
00217    else if (orderNb != 0)
00218       sh->SetUserLessThanFunction(userSuppliedLessThanFunction);
00219 
00220    if (nodup)
00221       sh->SetDropDuplicatePositions(true);
00222       
00223    while (l)
00224    { 
00225       nbFiles = l->size() ;
00226       if ( l->size() > 1 )
00227       {
00228          std::cout << "Sort list : " << nbFiles << " long" << std::endl;
00229  
00230          //---------------------------------------------------------
00231          sh->OrderFileList(l);  // sort the list (and compute ZSpacing !)
00232          //---------------------------------------------------------
00233  
00234          double zsp = sh->GetZSpacing();
00235          std::cout << "List sorted, ZSpacing = " << zsp << std::endl;
00236          break;  // The first one is OK. user will have to check
00237       }
00238       else
00239       {
00240          std::cout << "Oops! Empty 'Single Serie UID' FileSet found ?!?"
00241                    << std::endl;
00242       }
00243       l = sh->GetNextSingleSerieUIDFileSet();
00244    }
00245 
00246    if (check)
00247    {
00248       if ( !sh->IsCoherent(l) ) // just be sure (?)
00249       {
00250          std::cout << "Files are not coherent. Stop everything " << std::endl;
00251          sh->Delete();
00252          return 0;
00253       }
00254    }
00255 
00256    vtkGdcmReader *reader = vtkGdcmReader::New();
00257    reader->AllowLookupTableOff();
00258 
00259    if (mirror)
00260       reader->SetUserFunction (userSuppliedMirrorFunction);
00261    else if (upsidedown)
00262       reader->SetUserFunction (userSuppliedUpsideDownFunction);
00263 
00264    // Only the first FileList is dealt with (just an example)
00265    // (The files will not be parsed twice by the reader)
00266 
00267    //---------------------------------------------------------
00268    reader->SetCoherentFileList(l);
00269    //---------------------------------------------------------
00270 
00271    // because we passed a Coherent File List from a SerieHelper,
00272    // setting LoadMode is useless in this case
00273    //  reader->SetLoadMode(NO_SHADOWSEQ);  
00274    reader->Update();
00275 
00276    //print debug info:
00277    reader->GetOutput()->Print( cout );
00278 
00279    vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
00280 
00281    vtkImageViewer2 *viewer = vtkImageViewer2::New();
00282 
00283    if( reader->GetLookupTable() )
00284    {
00285       //convert to color:
00286       vtkImageMapToColors *map = vtkImageMapToColors::New ();
00287       map->SetInput (reader->GetOutput());
00288       map->SetLookupTable (reader->GetLookupTable());
00289       map->SetOutputFormatToRGB();
00290       viewer->SetInput ( map->GetOutput() );
00291       map->Delete();
00292    }
00293    else
00294    {
00295       vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
00296       viewer->SetColorLevel (0.5 * (range[1] + range[0]));
00297       viewer->SetColorWindow (range[1] - range[0]);
00298 
00299       viewer->SetInput ( reader->GetOutput() );
00300    }
00301    viewer->SetupInteractor (iren);
00302   
00303    //vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
00304    //viewer->SetColorWindow (range[1] - range[0]);
00305    //viewer->SetColorLevel (0.5 * (range[1] + range[0]));
00306 
00307    // Here is where we setup the observer, 
00308    vtkgdcmObserver *obs = vtkgdcmObserver::New();
00309    obs->ImageViewer = viewer;
00310    iren->AddObserver(vtkCommand::CharEvent,obs);
00311    obs->Delete();
00312 
00313    //viewer->Render();
00314    iren->Initialize();
00315    iren->Start();
00316 
00317    //if you wish you can export dicom to a vtk file  
00318    vtkStructuredPointsWriter *writer = vtkStructuredPointsWriter::New();
00319    writer->SetInput( reader->GetOutput());
00320    writer->SetFileName( "foo.vtk" );
00321    writer->SetFileTypeToBinary();
00322    //writer->Write();
00323 
00324    reader->Delete();
00325    iren->Delete();
00326    viewer->Delete();
00327    writer->Delete();
00328 
00329    return 0;
00330 }
00331 
00332 
00333 // --------------------------------------------------------
00334 // This is just a *very* simple example of user supplied function
00335 //      to mirror (why not ?) the image
00336 // It's *not* part of gdcm.
00337 // --------------------------------------------------------
00338 
00339 #define UF(ty)                          \
00340    int i, j;                            \
00341    ty *imj;                             \
00342    ty tamp;                             \
00343    for (j=0;j<ny;j++)                   \
00344    {                                    \
00345       imj = (ty *)im +j*nx;             \
00346       for (i=0;i<nx/2;i++)              \
00347       {                                 \
00348         tamp       =imj[i];             \
00349         imj[i]     =imj[nx-1-i];        \
00350         imj[nx-1-i]=tamp;               \
00351       }                                 \
00352    }                                    \
00353    if (nx%2 != 0)                       \
00354    {                                    \
00355       i = nx / 2;                       \
00356       for (j=0;j<ny;j++)                \
00357       {                                 \
00358         imj = (ty *)im  +j*nx;          \
00359         tamp       =imj[i];             \
00360         imj[i]     =imj[nx/2+1];        \
00361         imj[nx/2+1]=tamp;               \
00362       }                                 \
00363    }
00364 
00365 void userSuppliedMirrorFunction(uint8_t *im, GDCM_NAME_SPACE::File *f)
00366 {
00367    if (f->GetZSize() != 1)
00368    {
00369       std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
00370       return;
00371    }
00372 
00373    if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
00374    {
00375       std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
00376       return;
00377    }
00378    int nx = f->GetXSize();
00379    int ny = f->GetYSize();
00380 
00381    std::string pixelType = f->GetPixelType();
00382    if ( pixelType ==  "8U" || pixelType == "8S" )
00383    {
00384       UF(uint8_t)
00385       return;
00386    }
00387    if ( pixelType == "16U" || pixelType == "16S")
00388    {
00389       UF(uint16_t)
00390       return;
00391    }
00392    std::cout << "mirror : Pixel Size (!=8, !=16) not yet dealt with" 
00393              << std::endl;
00394    return;
00395 }
00396 
00397 
00398 // --------------------------------------------------------
00399 // This is just a *very* simple example of user supplied function
00400 //      to upsidedown (why not ?) the image
00401 // It's *not* part of gdcm.
00402 // --------------------------------------------------------
00403 
00404 #define UF2(ty)                         \
00405    int i, j;                            \
00406    ty *imj, *imJ;                       \
00407    ty tamp;                             \
00408    for (j=0;j<ny/2;j++)                 \
00409    {                                    \
00410       imj = (ty *)im +j*nx;             \
00411       imJ = (ty *)im +(ny-1-j)*nx;      \
00412       for (i=0;i<nx;i++)                \
00413       {                                 \
00414         tamp       =imj[i];             \
00415         imj[i]     =imJ[i];             \
00416         imJ[i]     =tamp;               \
00417       }                                 \
00418    }
00419 
00420 void userSuppliedUpsideDownFunction(uint8_t *im, GDCM_NAME_SPACE::File *f)
00421 {
00422    if (f->GetZSize() != 1)
00423    {
00424       std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
00425       return;
00426    }
00427 
00428    if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
00429    {
00430       std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
00431       return;
00432    }
00433    int nx = f->GetXSize();
00434    int ny = f->GetYSize();
00435 
00436    std::string pixelType = f->GetPixelType();
00437    if ( pixelType ==  "8U" || pixelType == "8S" )
00438    {
00439       UF2(uint8_t)
00440       return;
00441    }
00442    if ( pixelType == "16U" || pixelType == "16S")
00443    {
00444       UF2(uint16_t)
00445       return;
00446    }
00447    std::cout << "topdown : Pixel Size (!=8, !=16) not yet dealt with" 
00448              << std::endl;
00449    return;
00450 }
00451 
00452 // --------------------------------------------------------
00453 // This is just a *very* simple example of user supplied 'LessThan' function
00454 // It's *not* part of gdcm.
00455 //
00456 // Note : orderNb and elemsToOrderOn are here global variables.
00457 // Within a 'normal' function they would't be any orderNb and elemsToOrderOn var
00458 // User *knows* on what field(s) he wants to compare; 
00459 // He just writes a decent function.
00460 // Here, we want to get info from the command line Argument Manager.
00461 //
00462 // Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
00463 // --------------------------------------------------------
00464 
00465 
00466 bool userSuppliedLessThanFunction(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2)
00467 {
00468    // for *this* user supplied function, I supposed only ValEntries are checked.
00469 // 
00470    std::string s1, s2;
00471    GDCM_NAME_SPACE::DataEntry *e1,*e2;
00472    for (int ri=0; ri<orderNb; ri++)
00473    {
00474       std::cout << std::hex << elemsToOrderOn[2*ri] << "|" 
00475                             << elemsToOrderOn[2*ri+1]
00476                             << std::endl;
00477  
00478       e1= f1->GetDataEntry( elemsToOrderOn[2*ri],
00479                  elemsToOrderOn[2*ri+1]);
00480 
00481       e2= f2->GetDataEntry( elemsToOrderOn[2*ri],
00482                                  elemsToOrderOn[2*ri+1]);
00483       if(!e2 || !e2)
00484       {
00485          std::cout << std::hex << elemsToOrderOn[2*ri] << "|"
00486                               << elemsToOrderOn[2*ri+1]
00487                               << " not found" << std::endl;
00488          continue;
00489       }
00490       s1 = e1->GetString();      
00491       s2 = e2->GetString();
00492       std::cout << "[" << s1 << "] vs [" << s2 << "]" << std::endl;
00493       if ( s1 < s2 ) 
00494          return true;
00495       else if (s1 == s2 )
00496          continue;
00497       else 
00498          return false;
00499    }
00500    return false; // all fields equal
00501 }
00502 
00503 // --------------------------------------------------------
00504 // This is just an other *very* simple example of user supplied 'LessThan'
00505 // function
00506 // It's *not* part of gdcm.
00507 //
00508 // Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
00509 // --------------------------------------------------------
00510 
00511 bool userSuppliedLessThanFunction2(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2)
00512 {
00513    std::cout << "[" << f1->GetFileName() << "] vs [" 
00514                     << f2->GetFileName() << "]" << std::endl;
00515    return f1->GetFileName() < f2->GetFileName();
00516 }

Generated on Fri Aug 24 12:59:32 2007 for gdcm by  doxygen 1.4.6