]> Creatis software - gdcm.git/blob - vtk/vtkgdcmSerieViewer2.cxx
typo
[gdcm.git] / vtk / vtkgdcmSerieViewer2.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: vtkgdcmSerieViewer2.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/06/04 08:51:25 $
7   Version:   $Revision: 1.8 $
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 <vtkImageViewer2.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::File *f);
55 void userSuppliedUpsideDownFunction(uint8_t *im, gdcm::File *f);
56 bool userSuppliedLessThanFunction(gdcm::File *f1, gdcm::File *f2);
57 bool userSuppliedLessThanFunction2(gdcm::File *f1, gdcm::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 #if (VTK_MAJOR_VERSION >= 5)
89             int max = ImageViewer->GetSliceMax();
90             int slice = (ImageViewer->GetSlice() + 1 ) % ++max;
91             ImageViewer->SetSlice( slice );
92 #else
93             int max = ImageViewer->GetWholeZMax();
94             int slice = (ImageViewer->GetZSlice() + 1 ) % ++max;
95             ImageViewer->SetZSlice( slice );
96 #endif
97 #if !( (VTK_MAJOR_VERSION >= 5) || ( VTK_MAJOR_VERSION == 4 && VTK_MINOR_VERSION >= 5 ) )
98          // This used to be a bug in version VTK 4.4 and earlier
99             ImageViewer->GetRenderer()->ResetCameraClippingRange();
100 #endif
101             ImageViewer->Render();
102          }
103       }
104    }
105    vtkImageViewer2 *ImageViewer;
106 };
107
108 int main(int argc, char *argv[])
109 {
110    START_USAGE(usage)
111    " \n vtkgdcmSerieViewer2 : \n",
112    " Display a 'Serie' (same Serie UID) within a Directory                    ",
113    " You can navigate through the stack by hitting any character key.         ",
114    " usage: vtkgdcmSerieViewer dirname=sourcedirectory                        ",
115    "                           [noshadowseq][noshadow][noseq]                 ",
116    "                           [reverse] [{[mirror]|[topdown]|[rotate]}]      ",
117    "                           [order=] [check][debug]                        ",
118    "      sourcedirectory : name of the directory holding the images          ",
119    "                        if it holds more than one serie,                  ",
120    "                        only the first one is displayed.                  ",
121    "      noshadowseq: user doesn't want to load Private Sequences            ",
122    "      noshadow   : user doesn't want to load Private groups (odd number)  ",
123    "      noseq      : user doesn't want to load Sequences                    ",
124    "      reverse    : user wants to sort the images reverse order            ",
125    "      mirror     : user wants to 'mirror' the images    | just some simple",
126    "      upsidedown : user wants to 'upsidedown' the images| examples of user",
127    "                                                        | suppliedfunction",
128    "      check      : user wants to force more coherence checking            ",
129    "      order=     : group1-elem1,group2-elem2,... (in hexa, no space)      ",
130    "                   if we want to use them as a sort criterium             ",
131    "                   Right now : ValEntries only -just an example-          ",
132    "        or                                                                ",
133    "      order=     : order=name if we want to sort on file name (why not ?) ",
134    "      debug      : developper wants to run the program in 'debug mode'    ",
135    FINISH_USAGE
136
137
138    // Initialize Arguments Manager   
139    gdcm::ArgMgr *am= new gdcm::ArgMgr(argc, argv);
140   
141    if (argc == 1 || am->ArgMgrDefined("usage") )
142    {
143       am->ArgMgrUsage(usage); // Display 'usage'
144       delete am;
145       return 0;
146    }
147
148    char *dirName = am->ArgMgrWantString("dirname",usage);
149
150    int loadMode = gdcm::LD_ALL;
151    if ( am->ArgMgrDefined("noshadowseq") )
152       loadMode |= gdcm::LD_NOSHADOWSEQ;
153    else 
154    {
155       if ( am->ArgMgrDefined("noshadow") )
156          loadMode |= gdcm::LD_NOSHADOW;
157       if ( am->ArgMgrDefined("noseq") )
158          loadMode |= gdcm::LD_NOSEQ;
159    }
160
161    int reverse = am->ArgMgrDefined("reverse");
162
163    int mirror     = am->ArgMgrDefined("mirror");
164    int upsidedown = am->ArgMgrDefined("upsidedown");
165
166    if ( mirror && upsidedown )
167    {
168       std::cout << "*EITHER* mirror *OR* upsidedown !"
169                 << std::endl;
170       delete am;
171       return 0;
172    }
173
174    int check   = am->ArgMgrDefined("check");
175   
176    // This is so ugly, a cstring is NOT a char * (god damit!)
177    bool bname = ( strcmp(am->ArgMgrGetString("order", "not found"),"name")==0 );
178    if (bname)
179       elemsToOrderOn = am->ArgMgrGetXInt16Enum("order", &orderNb);
180
181    if (am->ArgMgrDefined("debug"))
182       gdcm::Debug::DebugOn();
183
184    /* if unused Param we give up */
185    if ( am->ArgMgrPrintUnusedLabels() )
186    {
187       am->ArgMgrUsage(usage);
188       delete am;
189       return 0;
190    } 
191
192    delete am;  // we don't need Argument Manager any longer
193
194    // ----------------------- End Arguments Manager ----------------------
195   
196    gdcm::SerieHelper *sh = gdcm::SerieHelper::New();
197    sh->SetLoadMode(loadMode);
198    if (reverse)
199       sh->SetSortOrderToReverse();
200    sh->SetDirectory( dirName, true);
201     
202    // Just to see
203
204    int nbFiles;
205    // For all the 'Single Serie UID' FileSets of the gdcm::Serie
206    gdcm::FileList *l = sh->GetFirstSingleSerieUIDFileSet();
207    if (l == 0 )
208    {
209       std::cout << "Oops! No 'Single Serie UID' FileSet found ?!?" << std::endl;
210       return 0;
211    }
212
213    if (bname)
214      sh->SetUserLessThanFunction(userSuppliedLessThanFunction2);
215    else if (orderNb != 0)
216       sh->SetUserLessThanFunction(userSuppliedLessThanFunction);
217
218    while (l)
219    { 
220       nbFiles = l->size() ;
221       if ( l->size() > 1 )
222       {
223          std::cout << "Sort list : " << nbFiles << " long" << std::endl;
224  
225          //---------------------------------------------------------
226          sh->OrderFileList(l);  // sort the list (and compute ZSpacing !)
227          //---------------------------------------------------------
228  
229          double zsp = sh->GetZSpacing();
230          std::cout << "List sorted, ZSpacing = " << zsp << std::endl;
231          break;  // The first one is OK. user will have to check
232       }
233       else
234       {
235          std::cout << "Oops! Empty 'Single Serie UID' FileSet found ?!?"
236                    << std::endl;
237       }
238       l = sh->GetNextSingleSerieUIDFileSet();
239    }
240
241    if (check)
242    {
243       if ( !sh->IsCoherent(l) ) // just be sure (?)
244       {
245          std::cout << "Files are not coherent. Stop everything " << std::endl;
246          sh->Delete();
247          return 0;
248       }
249    }
250
251    vtkGdcmReader *reader = vtkGdcmReader::New();
252    reader->AllowLookupTableOff();
253
254    if (mirror)
255       reader->SetUserFunction (userSuppliedMirrorFunction);
256    else if (upsidedown)
257       reader->SetUserFunction (userSuppliedUpsideDownFunction);
258
259    // Only the first FileList is dealt with (just an example)
260    // (The files will not be parsed twice by the reader)
261
262    //---------------------------------------------------------
263    reader->SetCoherentFileList(l);
264    //---------------------------------------------------------
265
266    // because we passed a Coherent File List from a SerieHelper,
267    // setting LoadMode is useless in this case
268    //  reader->SetLoadMode(NO_SHADOWSEQ);  
269    reader->Update();
270
271    //print debug info:
272    reader->GetOutput()->Print( cout );
273
274    vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
275
276    vtkImageViewer2 *viewer = vtkImageViewer2::New();
277
278    if( reader->GetLookupTable() )
279    {
280       //convert to color:
281       vtkImageMapToColors *map = vtkImageMapToColors::New ();
282       map->SetInput (reader->GetOutput());
283       map->SetLookupTable (reader->GetLookupTable());
284       map->SetOutputFormatToRGB();
285       viewer->SetInput ( map->GetOutput() );
286       map->Delete();
287    }
288    else
289    {
290       vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
291       viewer->SetColorLevel (0.5 * (range[1] + range[0]));
292       viewer->SetColorWindow (range[1] - range[0]);
293
294       viewer->SetInput ( reader->GetOutput() );
295    }
296    viewer->SetupInteractor (iren);
297   
298    //vtkFloatingPointType *range = reader->GetOutput()->GetScalarRange();
299    //viewer->SetColorWindow (range[1] - range[0]);
300    //viewer->SetColorLevel (0.5 * (range[1] + range[0]));
301
302    // Here is where we setup the observer, 
303    vtkgdcmObserver *obs = vtkgdcmObserver::New();
304    obs->ImageViewer = viewer;
305    iren->AddObserver(vtkCommand::CharEvent,obs);
306    obs->Delete();
307
308    //viewer->Render();
309    iren->Initialize();
310    iren->Start();
311
312    //if you wish you can export dicom to a vtk file  
313    vtkStructuredPointsWriter *writer = vtkStructuredPointsWriter::New();
314    writer->SetInput( reader->GetOutput());
315    writer->SetFileName( "foo.vtk" );
316    writer->SetFileTypeToBinary();
317    //writer->Write();
318
319    reader->Delete();
320    iren->Delete();
321    viewer->Delete();
322    writer->Delete();
323
324    return 0;
325 }
326
327
328 // --------------------------------------------------------
329 // This is just a *very* simple example of user supplied function
330 //      to mirror (why not ?) the image
331 // It's *not* part of gdcm.
332 // --------------------------------------------------------
333
334 #define UF(ty)                          \
335    int i, j;                            \
336    ty *imj;                             \
337    ty tamp;                             \
338    for (j=0;j<ny;j++)                   \
339    {                                    \
340       imj = (ty *)im +j*nx;             \
341       for (i=0;i<nx/2;i++)              \
342       {                                 \
343         tamp       =imj[i];             \
344         imj[i]     =imj[nx-1-i];        \
345         imj[nx-1-i]=tamp;               \
346       }                                 \
347    }                                    \
348    if (nx%2 != 0)                       \
349    {                                    \
350       i = nx / 2;                       \
351       for (j=0;j<ny;j++)                \
352       {                                 \
353         imj = (ty *)im  +j*nx;          \
354         tamp       =imj[i];             \
355         imj[i]     =imj[nx/2+1];        \
356         imj[nx/2+1]=tamp;               \
357       }                                 \
358    }
359
360 void userSuppliedMirrorFunction(uint8_t *im, gdcm::File *f)
361 {
362    if (f->GetZSize() != 1)
363    {
364       std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
365       return;
366    }
367
368    if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
369    {
370       std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
371       return;
372    }
373    int nx = f->GetXSize();
374    int ny = f->GetYSize();
375
376    std::string pixelType = f->GetPixelType();
377    if ( pixelType ==  "8U" || pixelType == "8S" )
378    {
379       UF(uint8_t)
380       return;
381    }
382    if ( pixelType == "16U" || pixelType == "16S")
383    {
384       UF(uint16_t)
385       return;
386    }
387    std::cout << "mirror : Pixel Size (!=8, !=16) not yet dealt with" 
388              << std::endl;
389    return;
390 }
391
392
393 // --------------------------------------------------------
394 // This is just a *very* simple example of user supplied function
395 //      to upsidedown (why not ?) the image
396 // It's *not* part of gdcm.
397 // --------------------------------------------------------
398
399 #define UF2(ty)                         \
400    int i, j;                            \
401    ty *imj, *imJ;                       \
402    ty tamp;                             \
403    for (j=0;j<ny/2;j++)                 \
404    {                                    \
405       imj = (ty *)im +j*nx;             \
406       imJ = (ty *)im +(ny-1-j)*nx;      \
407       for (i=0;i<nx;i++)                \
408       {                                 \
409         tamp       =imj[i];             \
410         imj[i]     =imJ[i];             \
411         imJ[i]     =tamp;               \
412       }                                 \
413    }
414
415 void userSuppliedUpsideDownFunction(uint8_t *im, gdcm::File *f)
416 {
417    if (f->GetZSize() != 1)
418    {
419       std::cout << "mirror : Multiframe images not yet dealt with" << std::endl;
420       return;
421    }
422
423    if (f->GetSamplesPerPixel() != 1 || f->GetBitsAllocated() == 24)
424    {
425       std::cout << "mirror : RGB / YBR not yet dealt with" << std::endl;
426       return;
427    }
428    int nx = f->GetXSize();
429    int ny = f->GetYSize();
430
431    std::string pixelType = f->GetPixelType();
432    if ( pixelType ==  "8U" || pixelType == "8S" )
433    {
434       UF2(uint8_t)
435       return;
436    }
437    if ( pixelType == "16U" || pixelType == "16S")
438    {
439       UF2(uint16_t)
440       return;
441    }
442    std::cout << "topdown : Pixel Size (!=8, !=16) not yet dealt with" 
443              << std::endl;
444    return;
445 }
446
447 // --------------------------------------------------------
448 // This is just a *very* simple example of user supplied 'LessThan' function
449 // It's *not* part of gdcm.
450 //
451 // Note : orderNb and elemsToOrderOn are here global variables.
452 // Within a 'normal' function they would't be any orderNb and elemsToOrderOn var
453 // User *knows* on what field(s) he wants to compare; 
454 // He just writes a decent function.
455 // Here, we want to get info from the command line Argument Manager.
456 //
457 // Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
458 // --------------------------------------------------------
459
460
461 bool userSuppliedLessThanFunction(gdcm::File *f1, gdcm::File *f2)
462 {
463    // for *this* user supplied function, I supposed only ValEntries are checked.
464 // 
465    std::string s1, s2;
466    gdcm::DataEntry *e1,*e2;
467    for (int ri=0; ri<orderNb; ri++)
468    {
469       std::cout << std::hex << elemsToOrderOn[2*ri] << "|" 
470                             << elemsToOrderOn[2*ri+1]
471                             << std::endl;
472  
473       e1= f1->GetDataEntry( elemsToOrderOn[2*ri],
474                  elemsToOrderOn[2*ri+1]);
475
476       e2= f2->GetDataEntry( elemsToOrderOn[2*ri],
477                                  elemsToOrderOn[2*ri+1]);
478       if(!e2 || !e2)
479       {
480          std::cout << std::hex << elemsToOrderOn[2*ri] << "|"
481                               << elemsToOrderOn[2*ri+1]
482                               << " not found" << std::endl;
483          continue;
484       }
485       s1 = e1->GetString();      
486       s2 = e2->GetString();
487       std::cout << "[" << s1 << "] vs [" << s2 << "]" << std::endl;
488       if ( s1 < s2 ) 
489          return true;
490       else if (s1 == s2 )
491          continue;
492       else 
493          return false;
494    }
495    return false; // all fields equal
496 }
497
498 // --------------------------------------------------------
499 // This is just an other *very* simple example of user supplied 'LessThan'
500 // function
501 // It's *not* part of gdcm.
502 //
503 // Warning : it's up to 'vtkgdcmSerieViewer' user to find a suitable data set !
504 // --------------------------------------------------------
505
506 bool userSuppliedLessThanFunction2(gdcm::File *f1, gdcm::File *f2)
507 {
508    std::cout << "[" << f1->GetFileName() << "] vs [" 
509                     << f2->GetFileName() << "]" << std::endl;
510    return f1->GetFileName() < f2->GetFileName();
511 }