vtkgdcmSerieViewer.cxx

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

Generated on Fri Aug 24 12:53:20 2007 for gdcm by  doxygen 1.4.6