]> Creatis software - gdcm.git/commitdiff
Since vtkImageViewer and vtkImageViewer2 don't behave the same way, I add
authorjpr <jpr>
Fri, 9 Dec 2005 12:54:05 +0000 (12:54 +0000)
committerjpr <jpr>
Fri, 9 Dec 2005 12:54:05 +0000 (12:54 +0000)
vtkgdcmSerieViewer2 (I keep vtkgdcmSerieViewer, user will choose the one he
likes more !)

vtk/CMakeLists.txt
vtk/vtkgdcmSerieViewer2.cxx [new file with mode: 0755]

index 72d0a74b25c4f0e612fd6a76a502d38b1cb3d71a..a0c5d334de0504f18e2f619dc98bc4fc0b6b905a 100644 (file)
@@ -73,6 +73,11 @@ TARGET_LINK_LIBRARIES(vtkgdcmSerieViewer
   vtkgdcm
   vtkRendering
 )
+ADD_EXECUTABLE(vtkgdcmSerieViewer2 ${GUI_EXECUTABLE} vtkgdcmSerieViewer2.cxx)
+TARGET_LINK_LIBRARIES(vtkgdcmSerieViewer2
+  vtkgdcm
+  vtkRendering
+)
 #-----------------------------------------------------------------------------
 SET(vtkWriteDicom_SOURCES
   vtkWriteDicom.cxx
diff --git a/vtk/vtkgdcmSerieViewer2.cxx b/vtk/vtkgdcmSerieViewer2.cxx
new file mode 100755 (executable)
index 0000000..4fcc69c
--- /dev/null
@@ -0,0 +1,502 @@
+/*=========================================================================
+                                                                                
+  Program:   gdcm
+  Module:    $RCSfile: vtkgdcmSerieViewer2.cxx,v $
+  Language:  C++
+  Date:      $Date: 2005/12/09 12:54:05 $
+  Version:   $Revision: 1.1 $
+                                                                                
+  Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
+  l'Image). All rights reserved. See Doc/License.txt or
+  http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
+                                                                                
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+                                                                                
+=========================================================================*/
+// This example illustrates how the vtkGdcmReader vtk class can 
+// use the result of gdcm::SerieHelper constructor and check
+// the various Setters :
+//     SerieHelper::SetOrderToReverse, 
+//     SerieHelper::SetUserLessThanFunction
+//     SerieHelper::SetLoadMode
+//     vtkGdcmReader::SetUserFunction
+//     vtkGdcmReader::SetCoherentFileList
+// Usage:
+//  * the Directory name that contains the Dicom images constituting the stack 
+//    should be given as command line argument (keyword : dirname=),
+//  * you can navigate through the stack by hitting any character key,
+//  * the produced vtk file is named "foo.vtk" (in the invocation directory).
+// 
+//----------------------------------------------------------------------------
+#include <vtkRenderWindowInteractor.h>
+#include <vtkImageViewer2.h>
+#include <vtkStructuredPoints.h>
+#include <vtkStructuredPointsWriter.h>
+#include <vtkCommand.h>
+#include <vtkRenderer.h>
+#include <vtkImageMapToColors.h>
+#include <vtkLookupTable.h>
+
+#include "vtkGdcmReader.h"
+#include "gdcmDocument.h"  // for NO_SHADOWSEQ
+#include "gdcmSerieHelper.h"
+#include "gdcmDebug.h"
+#include "gdcmDataEntry.h"
+
+#include "gdcmArgMgr.h" // for Argument Manager functions
+#include <string.h>     // for strcmp
+#ifndef vtkFloatingPointType
+#define vtkFloatingPointType float
+#endif
+
+void userSuppliedMirrorFunction (uint8_t *im, gdcm::File *f);
+void userSuppliedTopDownFunction(uint8_t *im, gdcm::File *f);
+bool userSuppliedLessThanFunction(gdcm::File *f1, gdcm::File *f2);
+bool userSuppliedLessThanFunction2(gdcm::File *f1, gdcm::File *f2);
+
+int orderNb;
+uint16_t *elemsToOrderOn;
+
+//----------------------------------------------------------------------------
+// Callback for the interaction
+class vtkgdcmObserver : public vtkCommand
+{
+public:
+   virtual char const *GetClassName() const 
+   { 
+      return "vtkgdcmObserver";
+   }
+
+   static vtkgdcmObserver *New() 
+   { 
+      return new vtkgdcmObserver; 
+   }
+
+   vtkgdcmObserver()
+   {
+      this->ImageViewer = NULL;
+   }
+
+   virtual void Execute(vtkObject *, unsigned long event, void* )
+   {
+      if ( this->ImageViewer )
+      {
+         if ( event == vtkCommand::CharEvent )
+         {
+            int max = ImageViewer->GetWholeZMax();
+            int slice = (ImageViewer->GetZSlice() + 1 ) % ++max;
+            ImageViewer->SetZSlice( slice );
+            ImageViewer->GetRenderer()->ResetCameraClippingRange();
+            ImageViewer->Render();
+         }
+      }
+   }
+   vtkImageViewer2 *ImageViewer;
+};
+
+int main(int argc, char *argv[])
+{
+   START_USAGE(usage)
+   " \n vtkgdcmSerieViewer2 : \n",
+   " Display a 'Serie' (same Serie UID) within a Directory                    ",
+   " You can navigate through the stack by hitting any character key.         ",
+   " usage: vtkgdcmSerieViewer dirname=sourcedirectory                        ",
+   "                           [noshadowseq][noshadow][noseq]                 ",
+   "                           [reverse] [{[mirror]|[topdown]|[rotate]}]      ",
+   "                           [order=] [check][debug]                        ",
+   "      sourcedirectory : name of the directory holding the images          ",
+   "                        if it holds more than one serie,                  ",
+   "                        only the first one id displayed.                  ",
+   "      noshadowseq: user doesn't want to load Private Sequences            ",
+   "      noshadow   : user doesn't want to load Private groups (odd number)  ",
+   "      noseq      : user doesn't want to load Sequences                    ",
+   "      reverse    : user wants to sort the images reverse order            ",
+   "      mirror     : user wants to 'mirror' the images | just some simple   ",
+   "      topdown    : user wants to 'topdown' the images| examples of user   ",
+   "      rotate     : NOT YET MADE (useless?)           | supplied functions ",
+   "      check      : user wants to force more coherence checking            ",
+   "      order=     : group1-elem1,group2-elem2,... (in hexa, no space)      ",
+   "                   if we want to use them as a sort criterium             ",
+   "                   Right now : ValEntries only -just an example-          ",
+   "        or                                                                ",
+   "      order=     : order=name if we want to sort on file name (why not ?) ",
+   "      debug      : user wants to run the program in 'debug mode'          ",
+   FINISH_USAGE
+
+
+   // Initialize Arguments Manager   
+   gdcm::ArgMgr *am= new gdcm::ArgMgr(argc, argv);
+  
+   if (argc == 1 || am->ArgMgrDefined("usage") )
+   {
+      am->ArgMgrUsage(usage); // Display 'usage'
+      delete am;
+      return 0;
+   }
+
+   char *dirName = am->ArgMgrWantString("dirname",usage);
+
+   int loadMode = gdcm::LD_ALL;
+   if ( am->ArgMgrDefined("noshadowseq") )
+      loadMode |= gdcm::LD_NOSHADOWSEQ;
+   else 
+   {
+      if ( am->ArgMgrDefined("noshadow") )
+         loadMode |= gdcm::LD_NOSHADOW;
+      if ( am->ArgMgrDefined("noseq") )
+         loadMode |= gdcm::LD_NOSEQ;
+   }
+
+   int reverse = am->ArgMgrDefined("reverse");
+
+   int mirror  = am->ArgMgrDefined("mirror");
+   int topdown = am->ArgMgrDefined("topdown");
+   int rotate  = am->ArgMgrDefined("rotate");
+
+   if ( mirror && topdown )
+   {
+      std::cout << "mirror *OR* topDown !"
+                << std::endl;
+      delete am;
+      return 0;
+   }
+   if ( rotate )
+   {
+      std::cout << "'rotate' undealt with -> ignored !"
+                << std::endl;
+   }
+
+   int check   = am->ArgMgrDefined("check");
+  
+   // This is so ugly, a cstring is NOT a char * (god damit!)
+   bool bname = ( strcmp(am->ArgMgrGetString("order", (char*)"not found"),"name")==0 );
+   if (bname)
+      elemsToOrderOn = am->ArgMgrGetXInt16Enum("order", &orderNb);
+
+   if (am->ArgMgrDefined("debug"))
+      gdcm::Debug::DebugOn();
+
+   /* if unused Param we give up */
+   if ( am->ArgMgrPrintUnusedLabels() )
+   {
+      am->ArgMgrUsage(usage);
+      delete am;
+      return 0;
+   } 
+
+   delete am;  // we don't need Argument Manager any longer
+
+   // ----------------------- End Arguments Manager ----------------------
+  
+   gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
+   sh->SetLoadMode(loadMode);
+   if (reverse)
+      sh->SetSortOrderToReverse();
+   sh->SetDirectory( dirName, true);
+    
+   // Just to see
+
+   int nbFiles;
+   // For all the 'Single Serie UID' FileSets of the gdcm::Serie
+   gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
+   if (l == 0 )
+   {
+      std::cout << "Oops! No 'Single Serie UID' FileSet found ?!?" << std::endl;
+      return 0;
+   }
+
+   if (bname)
+     sh->SetUserLessThanFunction(userSuppliedLessThanFunction2);
+   else if (orderNb != 0)
+      sh->SetUserLessThanFunction(userSuppliedLessThanFunction);
+
+   while (l)
+   { 
+      nbFiles = l->size() ;
+      if ( l->size() > 1 )
+      {
+         std::cout << "Sort list : " << nbFiles << " long" << std::endl;
+         sh->OrderFileList(l);  // sort the list
+         break;  // The first one is OK. user will have to check
+      }
+      else
+      {
+         std::cout << "Oops! Empty 'Single Serie UID' FileSet found ?!?"
+                   << std::endl;
+      }
+      l = sh->GetNextSingleSerieUIDFileSet();
+   }
+
+   if (check)
+   {
+      if ( !sh->IsCoherent(l) ) // just be sure (?)
+      {
+         std::cout << "Files are not coherent. Stop everything " << std::endl;
+         sh->Delete();
+         return 0;
+      }
+   }
+
+   vtkGdcmReader *reader = vtkGdcmReader::New();
+   reader->AllowLookupTableOff();
+
+   if (mirror)
+      reader->SetUserFunction (userSuppliedMirrorFunction);
+   else if (topdown)
+      reader->SetUserFunction (userSuppliedTopDownFunction);
+
+   // Only the first FileList is dealt with (just an example)
+   // (The files will not be parsed twice by the reader)
+
+   //---------------------------------------------------------
+   reader->SetCoherentFileList(l);
+   //---------------------------------------------------------
+
+   // because we passed a Coherent File List from a SerieHelper,
+   // setting LoadMode is useless in this case
+   //  reader->SetLoadMode(NO_SHADOWSEQ);  
+   reader->Update();
+
+   //print debug info:
+   reader->GetOutput()->Print( cout );
+
+   vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
+
+   vtkImageViewer2 *viewer = vtkImageViewer2::New();
+
+   if( reader->GetLookupTable() )
+   {
+      //convert to color:
+      vtkImageMapToColors *map = vtkImageMapToColors::New ();
+      map->SetInput (reader->GetOutput());
+      map->SetLookupTable (reader->GetLookupTable());
+      map->SetOutputFormatToRGB();
+      viewer->SetInput ( map->GetOutput() );
+      map->Delete();
+   }
+   else
+   {
+      vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
+      viewer->SetColorLevel (0.5 * (range[1] + range[0]));
+      viewer->SetColorWindow (range[1] - range[0]);
+
+      viewer->SetInput ( reader->GetOutput() );
+   }
+   viewer->SetupInteractor (iren);
+  
+   //vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
+   //viewer->SetColorWindow (range[1] - range[0]);
+   //viewer->SetColorLevel (0.5 * (range[1] + range[0]));
+
+   // Here is where we setup the observer, 
+   vtkgdcmObserver *obs = vtkgdcmObserver::New();
+   obs->ImageViewer = viewer;
+   iren->AddObserver(vtkCommand::CharEvent,obs);
+   obs->Delete();
+
+   //viewer->Render();
+   iren->Initialize();
+   iren->Start();
+
+   //if you wish you can export dicom to a vtk file  
+   vtkStructuredPointsWriter *writer = vtkStructuredPointsWriter::New();
+   writer->SetInput( reader->GetOutput());
+   writer->SetFileName( "foo.vtk" );
+   writer->SetFileTypeToBinary();
+   //writer->Write();
+
+   reader->Delete();
+   iren->Delete();
+   viewer->Delete();
+   writer->Delete();
+
+   return 0;
+}
+
+
+// --------------------------------------------------------
+// This is just a *very* simple example of user supplied function
+//      to mirror (why not ?) the image
+// It's *not* part of gdcm.
+// --------------------------------------------------------
+
+#define UF(ty)                          \
+   int i, j;                            \
+   ty *imj;                             \
+   ty tamp;                             \
+   for (j=0;j<ny;j++)                   \
+   {                                    \
+      imj = (ty *)im +j*nx;             \
+      for (i=0;i<nx/2;i++)              \
+      {                                 \
+        tamp       =imj[i];             \
+        imj[i]     =imj[nx-1-i];        \
+        imj[nx-1-i]=tamp;               \
+      }                                 \
+   }                                    \
+   if (nx%2 != 0)                       \
+   {                                    \
+      i = nx / 2;                       \
+      for (j=0;j<ny;j++)                \
+      {                                 \
+        imj = (ty *)im  +j*nx;          \
+        tamp       =imj[i];             \
+        imj[i]     =imj[nx/2+1];        \
+        imj[nx/2+1]=tamp;               \
+      }                                 \
+   }
+
+void userSuppliedMirrorFunction(uint8_t *im, gdcm::File *f)
+{
+   if (f->GetZSize() != 1)
+   {
+      std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
+      return;
+   }
+
+   if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
+   {
+      std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
+      return;
+   }
+   int nx = f->GetXSize();
+   int ny = f->GetYSize();
+
+   std::string pixelType = f->GetPixelType();
+   if ( pixelType ==  "8U" || pixelType == "8S" )
+   {
+      UF(uint8_t)
+      return;
+   }
+   if ( pixelType == "16U" || pixelType == "16S")
+   {
+      UF(uint16_t)
+      return;
+   }
+   std::cout << "mirror : Pixel Size (!=8, !=16) not yet dealt with" 
+             << std::endl;
+   return;
+}
+
+
+// --------------------------------------------------------
+// This is just a *very* simple example of user supplied function
+//      to topdown (why not ?) the image
+// It's *not* part of gdcm.
+// --------------------------------------------------------
+
+#define UF2(ty)                         \
+   int i, j;                            \
+   ty *imj, *imJ;                       \
+   ty tamp;                             \
+   for (j=0;j<ny/2;j++)                 \
+   {                                    \
+      imj = (ty *)im +j*nx;             \
+      imJ = (ty *)im +(ny-1-j)*nx;      \
+      for (i=0;i<nx;i++)                \
+      {                                 \
+        tamp       =imj[i];             \
+        imj[i]     =imJ[i];             \
+        imJ[i]     =tamp;               \
+      }                                 \
+   }
+
+void userSuppliedTopDownFunction(uint8_t *im, gdcm::File *f)
+{
+   if (f->GetZSize() != 1)
+   {
+      std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
+      return;
+   }
+
+   if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
+   {
+      std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
+      return;
+   }
+   int nx = f->GetXSize();
+   int ny = f->GetYSize();
+
+   std::string pixelType = f->GetPixelType();
+   if ( pixelType ==  "8U" || pixelType == "8S" )
+   {
+      UF2(uint8_t)
+      return;
+   }
+   if ( pixelType == "16U" || pixelType == "16S")
+   {
+      UF2(uint16_t)
+      return;
+   }
+   std::cout << "topdown : Pixel Size (!=8, !=16) not yet dealt with" 
+             << std::endl;
+   return;
+}
+
+// --------------------------------------------------------
+// This is just a *very* simple example of user supplied 'LessThan' function
+// It's *not* part of gdcm.
+//
+// Note : orderNb and elemsToOrderOn are here global variables.
+// Within a 'normal' function they would't be any orderNb and elemsToOrderOn var
+// User *knows* on what field(s) he wants to compare; 
+// He just writes a decent function.
+// Here, we want to get info from the command line Argument Manager.
+//
+// Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
+// --------------------------------------------------------
+
+
+bool userSuppliedLessThanFunction(gdcm::File *f1, gdcm::File *f2)
+{
+   // for *this* user supplied function, I supposed only ValEntries are checked.
+// 
+   std::string s1, s2;
+   gdcm::DataEntry *e1,*e2;
+   for (int ri=0; ri<orderNb; ri++)
+   {
+      std::cout << std::hex << elemsToOrderOn[2*ri] << "|" 
+                            << elemsToOrderOn[2*ri+1]
+                            << std::endl;
+      e1= f1->GetDataEntry( elemsToOrderOn[2*ri],
+                 elemsToOrderOn[2*ri+1]);
+
+      e2= f2->GetDataEntry( elemsToOrderOn[2*ri],
+                                 elemsToOrderOn[2*ri+1]);
+      if(!e2 || !e2)
+      {
+         std::cout << std::hex << elemsToOrderOn[2*ri] << "|"
+                              << elemsToOrderOn[2*ri+1]
+                              << " not found" << std::endl;
+         continue;
+      }
+      s1 = e1->GetString();      
+      s2 = e2->GetString();
+      std::cout << "[" << s1 << "] vs [" << s2 << "]" << std::endl;
+      if ( s1 < s2 ) 
+         return true;
+      else if (s1 == s2 )
+         continue;
+      else 
+         return false;
+   }
+   return false; // all fields equal
+}
+
+// --------------------------------------------------------
+// This is just an other *very* simple example of user supplied 'LessThan'
+// function
+// It's *not* part of gdcm.
+//
+// Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
+// --------------------------------------------------------
+
+bool userSuppliedLessThanFunction2(gdcm::File *f1, gdcm::File *f2)
+{
+   std::cout << "[" << f1->GetFileName() << "] vs [" 
+                    << f2->GetFileName() << "]" << std::endl;
+   return f1->GetFileName() < f2->GetFileName();
+}