]> Creatis software - gdcm.git/blob - vtk/vtkgdcmSerieViewer.cxx
e3de7efcdd3d727c7956b7258584f14bea3ed60b
[gdcm.git] / vtk / vtkgdcmSerieViewer.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: vtkgdcmSerieViewer.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/06/19 13:09:45 $
7   Version:   $Revision: 1.17 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                                                                                 
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18 // This example illustrates how the vtkGdcmReader vtk class can 
19 // use the result of gdcm::SerieHelper constructor and check
20 // the various Setters :
21 //     SerieHelper::SetOrderToReverse, 
22 //     SerieHelper::SetUserLessThanFunction
23 //     SerieHelper::SetLoadMode
24 //     vtkGdcmReader::SetUserFunction
25 //     vtkGdcmReader::SetCoherentFileList
26 // Usage:
27 //  * the Directory name that contains the Dicom images constituting the stack 
28 //    should be given as command line argument (keyword : dirname=),
29 //  * you can navigate through the stack by hitting any character key,
30 //  * the produced vtk file is named "foo.vtk" (in the invocation directory).
31 // 
32 //----------------------------------------------------------------------------
33 #include <vtkRenderWindowInteractor.h>
34 #include <vtkImageViewer.h>
35 #include <vtkStructuredPoints.h>
36 #include <vtkStructuredPointsWriter.h>
37 #include <vtkCommand.h>
38 #include <vtkRenderer.h>
39 #include <vtkImageMapToColors.h>
40 #include <vtkLookupTable.h>
41
42 #include "vtkGdcmReader.h"
43 #include "gdcmDocument.h"  // for NO_SHADOWSEQ
44 #include "gdcmSerieHelper.h"
45 #include "gdcmDebug.h"
46 #include "gdcmDataEntry.h"
47
48 #include "gdcmArgMgr.h" // for Argument Manager functions
49 #include <string.h>     // for strcmp
50 #ifndef vtkFloatingPointType
51 #define vtkFloatingPointType float
52 #endif
53
54 void userSuppliedMirrorFunction (uint8_t *im, GDCM_NAME_SPACE::File *f);
55 void userSuppliedTopDownFunction(uint8_t *im, GDCM_NAME_SPACE::File *f);
56 bool userSuppliedLessThanFunction(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2);
57 bool userSuppliedLessThanFunction2(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2);
58
59 int orderNb;
60 uint16_t *elemsToOrderOn;
61
62 //----------------------------------------------------------------------------
63 // Callback for the interaction
64 class vtkgdcmObserver : public vtkCommand
65 {
66 public:
67    virtual char const *GetClassName() const 
68    { 
69       return "vtkgdcmObserver";
70    }
71
72    static vtkgdcmObserver *New() 
73    { 
74       return new vtkgdcmObserver; 
75    }
76
77    vtkgdcmObserver()
78    {
79       this->ImageViewer = NULL;
80    }
81
82    virtual void Execute(vtkObject *, unsigned long event, void* )
83    {
84       if ( this->ImageViewer )
85       {
86          if ( event == vtkCommand::CharEvent )
87          {
88             int max = ImageViewer->GetWholeZMax();
89             int slice = (ImageViewer->GetZSlice() + 1 ) % ++max;
90             ImageViewer->SetZSlice( slice );
91             ImageViewer->GetRenderer()->ResetCameraClippingRange();
92             ImageViewer->Render();
93          }
94       }
95    }
96    vtkImageViewer *ImageViewer;
97 };
98
99 int main(int argc, char *argv[])
100 {
101    START_USAGE(usage)
102    " \n vtkgdcmSerieViewer : \n",
103    " Display a 'Serie' (same Serie UID) within a Directory                    ",
104    " You can navigate through the stack by hitting any character key.         ",
105    " usage: vtkgdcmSerieViewer dirname=sourcedirectory                        ",
106    "                           [noshadowseq][noshadow][noseq]                 ",
107    "                           [reverse] [{[mirror]|[topdown]|[rotate]}]      ",
108    "                           [order=] [check][debug]                        ",
109    "      sourcedirectory : name of the directory holding the images          ",
110    "                        if it holds more than one serie,                  ",
111    "                        only the first one id displayed.                  ",
112    "      noshadowseq: user doesn't want to load Private Sequences            ",
113    "      noshadow   : user doesn't want to load Private groups (odd number)  ",
114    "      noseq      : user doesn't want to load Sequences                    ",
115    "      reverse    : user wants to sort the images reverse order            ",
116    "      mirror     : user wants to 'mirror' the images | just some simple   ",
117    "      topdown    : user wants to 'topdown' the images| examples of user   ",
118    "      rotate     : NOT YET MADE (useless?)           | supplied functions ",
119    "      check      : user wants to force more coherence checking            ",
120    "      order=     : group1-elem1,group2-elem2,... (in hexa, no space)      ",
121    "                   if we want to use them as a sort criterium             ",
122    "                   Right now : ValEntries only -just an example-          ",
123    "        or                                                                ",
124    "      order=     : order=name if we want to sort on file name (why not ?) ",
125    "      debug      : user wants to run the program in 'debug mode'          ",
126    FINISH_USAGE
127
128
129    // Initialize Arguments Manager   
130    GDCM_NAME_SPACE::ArgMgr *am= new GDCM_NAME_SPACE::ArgMgr(argc, argv);
131   
132    if (argc == 1 || am->ArgMgrDefined("usage") )
133    {
134       am->ArgMgrUsage(usage); // Display 'usage'
135       delete am;
136       return 0;
137    }
138
139    char *dirName = am->ArgMgrWantString("dirname",usage);
140
141    int loadMode = GDCM_NAME_SPACE::LD_ALL;
142    if ( am->ArgMgrDefined("noshadowseq") )
143       loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ;
144    else 
145    {
146       if ( am->ArgMgrDefined("noshadow") )
147          loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW;
148       if ( am->ArgMgrDefined("noseq") )
149          loadMode |= GDCM_NAME_SPACE::LD_NOSEQ;
150    }
151
152    int reverse = am->ArgMgrDefined("reverse");
153
154    int mirror  = am->ArgMgrDefined("mirror");
155    int topdown = am->ArgMgrDefined("topdown");
156    int rotate  = am->ArgMgrDefined("rotate");
157
158    if ( mirror && topdown )
159    {
160       std::cout << "mirror *OR* topDown !"
161                 << std::endl;
162       delete am;
163       return 0;
164    }
165    if ( rotate )
166    {
167       std::cout << "'rotate' undealt with -> ignored !"
168                 << std::endl;
169    }
170
171    int check   = am->ArgMgrDefined("check");
172   
173    // This is so ugly, a cstring is NOT a char * (god damit!)
174    bool bname = ( strcmp(am->ArgMgrGetString("order", "not found"),"name")==0 );
175    if (bname)
176       elemsToOrderOn = am->ArgMgrGetXInt16Enum("order", &orderNb);
177
178    if (am->ArgMgrDefined("debug"))
179       GDCM_NAME_SPACE::Debug::DebugOn();
180
181    /* if unused Param we give up */
182    if ( am->ArgMgrPrintUnusedLabels() )
183    {
184       am->ArgMgrUsage(usage);
185       delete am;
186       return 0;
187    } 
188
189    delete am;  // we don't need Argument Manager any longer
190
191    // ----------------------- End Arguments Manager ----------------------
192   
193    GDCM_NAME_SPACE::SerieHelper *sh = GDCM_NAME_SPACE::SerieHelper::New();
194    sh->SetLoadMode(loadMode);
195    if (reverse)
196       sh->SetSortOrderToReverse();
197    sh->SetDirectory( dirName, true);
198     
199    // Just to see
200
201    int nbFiles;
202    // For all the 'Single Serie UID' FileSets of the gdcm::Serie
203    GDCM_NAME_SPACE::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
204    if (l == 0 )
205    {
206       std::cout << "Oops! No 'Single Serie UID' FileSet found ?!?" << std::endl;
207       return 0;
208    }
209
210    if (bname)
211      sh->SetUserLessThanFunction(userSuppliedLessThanFunction2);
212    else if (orderNb != 0)
213       sh->SetUserLessThanFunction(userSuppliedLessThanFunction);
214
215    while (l)
216    { 
217       nbFiles = l->size() ;
218       if ( l->size() > 1 )
219       {
220          std::cout << "Sort list : " << nbFiles << " long" << std::endl;
221          sh->OrderFileList(l);  // sort the list
222          break;  // The first one is OK. user will have to check
223       }
224       else
225       {
226          std::cout << "Oops! Empty 'Single Serie UID' FileSet found ?!?"
227                    << std::endl;
228       }
229       l = sh->GetNextSingleSerieUIDFileSet();
230    }
231
232    if (check)
233    {
234       if ( !sh->IsCoherent(l) ) // just be sure (?)
235       {
236          std::cout << "Files are not coherent. Stop everything " << std::endl;
237          sh->Delete();
238          return 0;
239       }
240    }
241
242    vtkGdcmReader *reader = vtkGdcmReader::New();
243    reader->AllowLookupTableOff();
244
245    if (mirror)
246       reader->SetUserFunction (userSuppliedMirrorFunction);
247    else if (topdown)
248       reader->SetUserFunction (userSuppliedTopDownFunction);
249
250    // Only the first FileList is dealt with (just an example)
251    // (The files will not be parsed twice by the reader)
252
253    //---------------------------------------------------------
254    reader->SetCoherentFileList(l);
255    //---------------------------------------------------------
256
257    // because we passed a Coherent File List from a SerieHelper,
258    // setting LoadMode is useless in this case
259    //  reader->SetLoadMode(NO_SHADOWSEQ);  
260    reader->Update();
261
262    //print debug info:
263    reader->GetOutput()->Print( cout );
264
265    vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
266
267    vtkImageViewer *viewer = vtkImageViewer::New();
268
269    if( reader->GetLookupTable() )
270    {
271       //convert to color:
272       vtkImageMapToColors *map = vtkImageMapToColors::New ();
273       map->SetInput (reader->GetOutput());
274       map->SetLookupTable (reader->GetLookupTable());
275       map->SetOutputFormatToRGB();
276       viewer->SetInput ( map->GetOutput() );
277       map->Delete();
278    }
279    else
280    {
281       vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
282       viewer->SetColorLevel (0.5 * (range[1] + range[0]));
283       viewer->SetColorWindow (range[1] - range[0]);
284
285       viewer->SetInput ( reader->GetOutput() );
286    }
287    viewer->SetupInteractor (iren);
288   
289    //vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
290    //viewer->SetColorWindow (range[1] - range[0]);
291    //viewer->SetColorLevel (0.5 * (range[1] + range[0]));
292
293    // Here is where we setup the observer, 
294    vtkgdcmObserver *obs = vtkgdcmObserver::New();
295    obs->ImageViewer = viewer;
296    iren->AddObserver(vtkCommand::CharEvent,obs);
297    obs->Delete();
298
299    //viewer->Render();
300    iren->Initialize();
301    iren->Start();
302
303    //if you wish you can export dicom to a vtk file  
304    vtkStructuredPointsWriter *writer = vtkStructuredPointsWriter::New();
305    writer->SetInput( reader->GetOutput());
306    writer->SetFileName( "foo.vtk" );
307    writer->SetFileTypeToBinary();
308    //writer->Write();
309
310    reader->Delete();
311    iren->Delete();
312    viewer->Delete();
313    writer->Delete();
314
315    return 0;
316 }
317
318
319 // --------------------------------------------------------
320 // This is just a *very* simple example of user supplied function
321 //      to mirror (why not ?) the image
322 // It's *not* part of gdcm.
323 // --------------------------------------------------------
324
325 #define UF(ty)                          \
326    int i, j;                            \
327    ty *imj;                             \
328    ty tamp;                             \
329    for (j=0;j<ny;j++)                   \
330    {                                    \
331       imj = (ty *)im +j*nx;             \
332       for (i=0;i<nx/2;i++)              \
333       {                                 \
334         tamp       =imj[i];             \
335         imj[i]     =imj[nx-1-i];        \
336         imj[nx-1-i]=tamp;               \
337       }                                 \
338    }                                    \
339    if (nx%2 != 0)                       \
340    {                                    \
341       i = nx / 2;                       \
342       for (j=0;j<ny;j++)                \
343       {                                 \
344         imj = (ty *)im  +j*nx;          \
345         tamp       =imj[i];             \
346         imj[i]     =imj[nx/2+1];        \
347         imj[nx/2+1]=tamp;               \
348       }                                 \
349    }
350
351 void userSuppliedMirrorFunction(uint8_t *im, GDCM_NAME_SPACE::File *f)
352 {
353    if (f->GetZSize() != 1)
354    {
355       std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
356       return;
357    }
358
359    if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
360    {
361       std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
362       return;
363    }
364    int nx = f->GetXSize();
365    int ny = f->GetYSize();
366
367    std::string pixelType = f->GetPixelType();
368    if ( pixelType ==  "8U" || pixelType == "8S" )
369    {
370       UF(uint8_t)
371       return;
372    }
373    if ( pixelType == "16U" || pixelType == "16S")
374    {
375       UF(uint16_t)
376       return;
377    }
378    std::cout << "mirror : Pixel Size (!=8, !=16) not yet dealt with" 
379              << std::endl;
380    return;
381 }
382
383
384 // --------------------------------------------------------
385 // This is just a *very* simple example of user supplied function
386 //      to topdown (why not ?) the image
387 // It's *not* part of gdcm.
388 // --------------------------------------------------------
389
390 #define UF2(ty)                         \
391    int i, j;                            \
392    ty *imj, *imJ;                       \
393    ty tamp;                             \
394    for (j=0;j<ny/2;j++)                 \
395    {                                    \
396       imj = (ty *)im +j*nx;             \
397       imJ = (ty *)im +(ny-1-j)*nx;      \
398       for (i=0;i<nx;i++)                \
399       {                                 \
400         tamp       =imj[i];             \
401         imj[i]     =imJ[i];             \
402         imJ[i]     =tamp;               \
403       }                                 \
404    }
405
406 void userSuppliedTopDownFunction(uint8_t *im, GDCM_NAME_SPACE::File *f)
407 {
408    if (f->GetZSize() != 1)
409    {
410       std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
411       return;
412    }
413
414    if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
415    {
416       std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
417       return;
418    }
419    int nx = f->GetXSize();
420    int ny = f->GetYSize();
421
422    std::string pixelType = f->GetPixelType();
423    if ( pixelType ==  "8U" || pixelType == "8S" )
424    {
425       UF2(uint8_t)
426       return;
427    }
428    if ( pixelType == "16U" || pixelType == "16S")
429    {
430       UF2(uint16_t)
431       return;
432    }
433    std::cout << "topdown : Pixel Size (!=8, !=16) not yet dealt with" 
434              << std::endl;
435    return;
436 }
437
438 // --------------------------------------------------------
439 // This is just a *very* simple example of user supplied 'LessThan' function
440 // It's *not* part of gdcm.
441 //
442 // Note : orderNb and elemsToOrderOn are here global variables.
443 // Within a 'normal' function they would't be any orderNb and elemsToOrderOn var
444 // User *knows* on what field(s) he wants to compare; 
445 // He just writes a decent function.
446 // Here, we want to get info from the command line Argument Manager.
447 //
448 // Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
449 // --------------------------------------------------------
450
451
452 bool userSuppliedLessThanFunction(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2)
453 {
454    // for *this* user supplied function, I supposed only ValEntries are checked.
455 // 
456    std::string s1, s2;
457    GDCM_NAME_SPACE::DataEntry *e1,*e2;
458    for (int ri=0; ri<orderNb; ri++)
459    {
460       std::cout << std::hex << elemsToOrderOn[2*ri] << "|" 
461                             << elemsToOrderOn[2*ri+1]
462                             << std::endl;
463  
464       e1= f1->GetDataEntry( elemsToOrderOn[2*ri],
465                  elemsToOrderOn[2*ri+1]);
466
467       e2= f2->GetDataEntry( elemsToOrderOn[2*ri],
468                                  elemsToOrderOn[2*ri+1]);
469       if(!e2 || !e2)
470       {
471          std::cout << std::hex << elemsToOrderOn[2*ri] << "|"
472                               << elemsToOrderOn[2*ri+1]
473                               << " not found" << std::endl;
474          continue;
475       }
476       s1 = e1->GetString();      
477       s2 = e2->GetString();
478       std::cout << "[" << s1 << "] vs [" << s2 << "]" << std::endl;
479       if ( s1 < s2 ) 
480          return true;
481       else if (s1 == s2 )
482          continue;
483       else 
484          return false;
485    }
486    return false; // all fields equal
487 }
488
489 // --------------------------------------------------------
490 // This is just an other *very* simple example of user supplied 'LessThan'
491 // function
492 // It's *not* part of gdcm.
493 //
494 // Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
495 // --------------------------------------------------------
496
497 bool userSuppliedLessThanFunction2(GDCM_NAME_SPACE::File *f1, GDCM_NAME_SPACE::File *f2)
498 {
499    std::cout << "[" << f1->GetFileName() << "] vs [" 
500                     << f2->GetFileName() << "]" << std::endl;
501    return f1->GetFileName() < f2->GetFileName();
502 }