From: jpr Date: Fri, 9 Dec 2005 12:54:05 +0000 (+0000) Subject: Since vtkImageViewer and vtkImageViewer2 don't behave the same way, I add X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=1d970a29c1894bc7fdb87630f3ea8b7b9a85ac03;p=gdcm.git Since vtkImageViewer and vtkImageViewer2 don't behave the same way, I add vtkgdcmSerieViewer2 (I keep vtkgdcmSerieViewer, user will choose the one he likes more !) --- diff --git a/vtk/CMakeLists.txt b/vtk/CMakeLists.txt index 72d0a74b..a0c5d334 100644 --- a/vtk/CMakeLists.txt +++ b/vtk/CMakeLists.txt @@ -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 index 00000000..4fcc69c6 --- /dev/null +++ b/vtk/vtkgdcmSerieViewer2.cxx @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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 // 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;jGetZSize() != 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;jGetZSize() != 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; riGetDataEntry( 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(); +}