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