File: TutorialTest.cxx

package info (click to toggle)
itksnap 3.4.0-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 10,196 kB
  • ctags: 9,196
  • sloc: cpp: 62,895; sh: 175; makefile: 13
file content (373 lines) | stat: -rw-r--r-- 18,790 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    $RCSfile: TutorialTest.cxx,v $
  Language:  C++
  Date:      $Date: 2006/12/02 04:22:20 $
  Version:   $Revision: 1.1 $
  Copyright (c) 2003 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm 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.
=========================================================================*/
// Borland compiler is very lazy so we need to instantiate the template
//  by hand 
#if defined(__BORLANDC__)
#include <SNAPBorlandDummyTypes.h>
#endif

#include "ImageIORoutines.h"
#include "IRISApplication.h"
#include "IRISImageData.h"
#include "LevelSetMeshPipeline.h"
#include "SNAPImageData.h"

#include "vtkPolyData.h"
#include "vtkPolyDataWriter.h"

#include <vector>

/** 
 * This function is designed to serve as a tutorial through the classes 
 * that form the logical framework of SNAP. In this test, the student will
 * be able to load images, perform preprocessing, initialize and run segmentation,
 * output slices from the segmentation and relabel the segmentatinon
 *
 * The method should be invoked with two parameters. First is the full path to
 * the file MRIcrop-orig.gipl which contains the test image. Second is the full
 * path to the label file MRIcrop-seg.label. Both of these files can be downloaded
 * from the SNAP website 
 */
int main(int argc, char *argv[]) 
{
  if(argc < 3)
    {
    cerr << "Must specify two arguments." << endl;
    return 1;
    }
  /* =======================================================================
   * Section 1. Starting the Application and Loading an Image
   * ====================================================================== */

  /* We begin by creating an IRISApplication object. This is the most high
   * level object in the SNAP class hierarchy and it provides access to the 
   * remainder of the SNAP logic classes */
  IRISApplication application;

  /* Next we will load the test image. In this example it is expected that the
   * image MRIcrop-orig.gipl, which can be downloaded from the SNAP website 
   * will be used. The user will pass the full path to this file as the first
   * parameter to the program */
  const char *sImageFileName = argv[1];
  
  try {
    /* The responsibility for loading the image is with the user. First, we 
     * create an itk::Image object into which the image will be loaded from
     * disk */
    IRISApplication::GreyImageType::Pointer imgGrey;

    /* SNAP provides a helper method LoadImageFromFile for loading images */ 
    LoadImageFromFile(sImageFileName,imgGrey);

    /* Once the image has been loaded, it can be passed on to the IRISApplication.
     * However, SNAP needs to know the orientation of the image, which is 
     * expressed as the position of the image's origin {X,Y,Z}={0,0,0} in 
     * patient coordinates. This information is passed on in the form of a 
     * three letter RAI code. The value "ASR" means that the origin lies in the
     * Anterior-Superior-Right corner of the patient coordinate system, with
     * X axis corresponding to the Anterior-Posterior axis, Y axis corresponding
     * to the Inferior-Superior axis and Z to the Right-Left axis. For the image
     * we are loading, the correct code is "RAI" */
    application.UpdateIRISGreyImage(imgGrey,"RAI");

    /* In addition to loading the image, we are going to load the segmentation
     * labels associated with this image. Segmentation labels are stored in the 
     * file MRIcrop-seg.label and passed on as an argument to this test program */
    const char *sLabelFileName = argv[2];
    application.GetColorLabelTable()->LoadFromFile(sLabelFileName);
  }
  catch(itk::ExceptionObject &exception)
  {
    // This code is called if LoadImageFromFile fails.
    cout << "Error Loading Image: " << exception;
    return -1;
  }

  /* =======================================================================
   * Section 2. Accessing Image Data Using IRISImageData Class
   * ====================================================================== */
                                      
  /* Now the image has been loaded into IRISApplication. IRISApplication stores
   * its data using two high level classes: IRISImageData and SNAPImageData. 
   * IRISImageData holds the original greyscale image that we have loaded as
   * well as the labelling of that image that is constructed throughout the 
   * segmentation. SNAPImageData is used for automatic segmentation and contains
   * a subregion of the original greyscale image. At this point SNAPImageData has
   * not yet been allocated. SNAPImageData is a child class of IRISImageData.
   *
   * The method IRISApplication::GetCurrentImageData() returns the IRISImageData
   * object associated with the current state of SNAP. In manual state (current
   * state right now) it returns a pointer to an IRISImageData object. In
   * automatic segmentation state, it returns a pointer to a SNAPImageData 
   * object.
   *
   * The IRISImageData and SNAPImageData objects can also be accessed explicitly 
   * using the GetIRISImageData() and GetSNAPImageData() pointers.
   *
   * We will begin by examining the IRISImageData class. We can use it to access
   * the underlying image and to examine the segmentation. */
  IRISImageData *irisData = application.GetCurrentImageData();

  /* IRISImageData contains two images: the grey level image and a label or 
   * segmentation image. Both images are of the same dimensions. The grey image
   * has voxels of type GreyType and the segmentation image of type LabelType */
  cout << "Image Dimensions: " << 
    to_int(irisData->GetVolumeExtents()) << endl;       // Ignore the to_int!
//  cout << "Voxel Size      : " << irisData->GetVoxelScaleFactor() << endl;

  /* IRISImageData also handles segmentation labels. We can inquire about each label. 
   * Labels are indexed from 1 to 255 (0 is the clear label). Only some labels are 
   * defined as 'valid', i.e., available to the user to edit */
  for(unsigned int iLabel=0; iLabel < MAX_COLOR_LABELS; iLabel++)
    {
    /* Labels are represented by the class ColorLabel. Each label has a color,
     * a transparency level, a visible flag, a 'visible in 3d mesh flag', and a
     * textual description */
    const ColorLabel &label = application.GetColorLabelTable()->GetColorLabel(iLabel);                                                       
    cout << "Label Number  : " << iLabel << endl;
    cout << "  Decription  : " << label.GetLabel() << endl;
    cout << "  Color       : {" 
      << (int) label.GetRGB(0) << "," 
      << (int) label.GetRGB(1) << "," 
      << (int) label.GetRGB(2) << "}" << endl;
    cout << "  Opacity     : " << label.GetAlpha() << endl;
    }

  /* The current drawing label and the current draw-over label are properties of
   * the SNAP application and are accessed using the GlobalState class.
   * GlobalState stores a great number of application-wide settings */
  application.GetGlobalState()->SetDrawingColorLabel(7);  // caudate label
  application.GetGlobalState()->SetCoverageMode(PAINT_OVER_ALL);

  /* The next class we will learn about is the ImageWrapper class, which is a
   * wrapper around the standard itk::Image class. The ImageWrapper provides
   * several mini-pipelines involving the itk::Image stored within. First
   * let's access the grey images' wrapper in IRISImageData */
  GreyImageWrapper *wrapGrey = irisData->GetGrey();

  /* The class GreyImageWrapper is a subclass of the generic templated class 
   * ImageWrapper<TPixel> with template argument TPixel=GreyType. We can
   * use ImageWrapper to get to the itk::Image itself */
  GreyImageWrapper::ImagePointer imgGrey = wrapGrey->GetImage();

  /* Here are some of the methods available through the ImageWrapper class */
  cout << "Grey Min        : " << wrapGrey->GetImageMin() << endl;
  cout << "Grey Max        : " << wrapGrey->GetImageMax() << endl;

  /* The IRISImageData and ImageWrapper classes keep track of the current 
   * position of the SNAP 3D cursor. When an image is loaded, the cursor is 
   * positioned in the center of the image, as we can see by querying the 
   * ImageWrapper class */
  Vector3ui vCursor = wrapGrey->GetSliceIndex();
  cout << "Cursor Position : " << to_int(vCursor) << endl; // Ignore the to_int!
  cout << "Value at Cursor : " 
    << wrapGrey->GetVoxel(vCursor) << endl;

  /* The cursor position obtained above is given in image coordinates, where
   * Z is the slice number, Y is the row number and  X is the column number. 
   * In order to specify slice coordinates in patient (anatomical)
   * coordinates, we need to know the correct transformation, which we had \
   * specified using an 'RAI' code when loading the grey image. The 
   * transformation can be accessed through the IRISApplication object */
  ImageCoordinateGeometry geometry = irisData->GetImageGeometry();
  Vector3ui vCursorPatient = 
    geometry.GetImageToAnatomyTransform().TransformVoxelIndex(vCursor);
  cout << "Anatomical Posn.: " << to_int(vCursorPatient) << endl;

  /* The cursor position is the same for the grey image wrapper and the label
   * image wrapper. Hence, to set the cursor position, we must call the 
   * appropriate method in IRISImageData. We can also call the method
   * IRISApplication::SetCursorPosition for the same effect. */
  vCursor -= Vector3ui(1,0,0);
  irisData->SetCrosshairs(vCursor);

  /* ImageWrapper provides a way to extract orthogonal 2D slices form the
   * image. A slice is extracted by setting the crosshair position and calling
   * the GetDisplaySlice method. This returns a 2D image of type unsigned char
   * that can be displayed on the screen.
   *
   * The mapping between the intensities in the grey image (which are shorts) 
   * and intensities in the slice (which are bytes) can be changed by calling 
   * the method SetIntensityMapFunction() */
  GreyImageWrapper::DisplaySlicePointer imgSlice = wrapGrey->GetDisplaySlice(0);

  /* So far, we have looked at the grey image wrapper. The segmentation image 
   * wrapper is very similar, except that it produces slices that are RGB 
   * images.
   *
   * When we loaded the grey image, the segmentation image in IRISImageData was
   * automatically initialized to the same size and filled with voxels of 
   * label 0, which is the 'Clear' label in SNAP. To update the segmentation, 
   * we can directly modify the image wrapped by the LabelImageWrapper or
   * we can use the GetVoxelForUpdate() method: */
  irisData->GetSegmentation()->GetVoxelForUpdate(vCursor) = 2;
  LabelImageWrapper::DisplaySlicePointer imgRGBSlice = 
    irisData->GetSegmentation()->GetDisplaySlice(0);

  /* For validation purposes, we are going to write to disk the grey and 
   * segmentation slices that we've extracted in this Section */
  SaveImageToFile("greyslice01.png",imgSlice.GetPointer());
  SaveImageToFile("rgbslice01.png",imgRGBSlice.GetPointer());
  
  /* =======================================================================
   * Section 3. Automatic Segmentation Mode
   * ====================================================================== */
                                      
  /* To begin automatic segmentation, we need to switch on the automatic 
   * segmentation mode. In order to do that, we must specify the region of
   * interest that we want to pass on to this mode and whether or not we 
   * want to perform resampling on the region of interest. In this case we
   * will use the entire image as the region of interest and do no resampling */
  SNAPSegmentationROISettings roiSettings;
  roiSettings.SetROI(irisData->GetImageRegion());
  roiSettings.SetResampleFlag(false);
  
  /* We can now tell the IRISApplication to initialize the SNAPImageData object.
   * This method takes the ROI settings as the first parameter and an 
   * itk::Command object as the optional second parameter. The Command is used
   * as a callback during the resampling, and is provided primarify for GUIs 
   * that want to display a progress bar. */
  application.InitializeSNAPImageData(roiSettings);

  /* Once the SNAP data is initalized, we can instruct the IRISApplication to 
   * use it as the current image data, in other words, to switch to the auto
   * segmentation mode */
  application.SetCurrentImageDataToSNAP();

  /* The SNAPImageData class inherits the methods and attributes of the 
   * IRISImageData class and provides additional functionality related to 
   * level set image segmentation. In addition to the Grey and Segmentation
   * images (and image wrappers), the SNAPImageData class contains a Speed
   * image wrapper, a SnakeInitialization image wrapper and a Snake image
   * wrapper. All three of these image wrappers contain itk::Image objects
   * with pixel type float. In addition, the SNAPImageData class provides 
   * method for running the automatic segmetnation pipeline */
  SNAPImageData *snapData = application.GetSNAPImageData();

  /* Automatic segmentation begins with preprocessing. We must choose the type 
   * of preprocessing to apply and the parameters of the preprocessing. In this
   * tutorial we will use edge-based snakes and edge-detection preprocessing */
  EdgePreprocessingSettings edgeSettings = 
    EdgePreprocessingSettings::MakeDefaultSettings();
  edgeSettings.SetGaussianBlurScale(0.6);
  edgeSettings.SetRemappingSteepness(0.02);
  edgeSettings.SetRemappingExponent(2.0);
  
  /* The preprocessing is performed when we call the DoEdgePreprocessing method.
   * the first argument is an EdgePreprocessingSettings object and the second
   * optional argument is a Command that can be used to implement a progress bar*/
  snapData->DoEdgePreprocessing(edgeSettings);

  /* The result of the preprocessing is stored in the Speed image wrapper in
   * the SNAPImageData class. We can write out a slice of the preprocessed image */
  SpeedImageWrapper *wrapSpeed = snapData->GetSpeed();
  SaveImageToFile("speedslice00.mha",wrapSpeed->GetDisplaySlice(0));

  /* The next step after preprocessing is to initialize the segmentation with
   * bubbles. This is performed by passing an array of bubbles to the method
   * SNAPImageData::InitializeSegmentationPipeline. The bubbles given below
   * are located in the caudate nuclei of our image */
  std::vector<Bubble> bubbles(2);
  bubbles[0].center = Vector3i(50,29,26); bubbles[0].radius = 3;
  bubbles[1].center = Vector3i(50,57,34); bubbles[1].radius = 2;

  /* In addition to the bubble array, the method InitializeSegmentationPipeline
   * requires a set of level set segmentation parameters, which are stored in 
   * the SnakeParameters class */
  SnakeParameters parameters = SnakeParameters::GetDefaultEdgeParameters();

  /* In order for the snake to converge, we enable the advection force */
  parameters.SetAdvectionWeight(5.0);

  /* We can now initialize the segmentation pipeline. In addition to the 
   * snake parameters and the bubbles, we need to specify the color label to 
   * be used for the snake-based segmentation */
  snapData->InitializeSegmentation(
    parameters,bubbles,application.GetGlobalState()->GetDrawingColorLabel());

  /* Now the pipeline is initialized and ready to run. Run 250 iterations */
  snapData->RunSegmentation(250);

  /* We can also rewind the segmentation */
  snapData->RestartSegmentation();

  /* And we can change the parameters on the fly */
  snapData->RunSegmentation(100);
  parameters.SetAdvectionWeight(3.0);
  snapData->SetSegmentationParameters(parameters);
  snapData->RunSegmentation(200);

  /* Now the segmentation is done. The Snake image wrapper contains a floating
   * point image whose positive voxels correspond to the pixels outside of the
   * segmentation boundary and whose negative valued pixels are inside. In 
   * to get an actual snake surface, we can use the LevelSetMeshPipeline object,
   * which uses VTK to trace the zero-level-set of the Snake image */  
  LevelSetMeshPipeline meshPipeline;    
  meshPipeline.SetImage(snapData->GetSnake()->GetImage());
  // meshPipeline.SetImage(snapData->GetLevelSetImage());

  /* Pass the globally stored mesh options to the mesh pipeline */
  meshPipeline.SetMeshOptions(application.GetGlobalState()->GetMeshOptions());
  
  /* Create a vtkPolyData to store the resulting mesh, and run the pipeline */
  vtkPolyData *meshResult = vtkPolyData::New();  
  meshPipeline.ComputeMesh(meshResult);

  /* Export the mesh to VTK format */
  vtkPolyDataWriter *polyWriter = vtkPolyDataWriter::New();
  polyWriter->SetInput(meshResult);
  polyWriter->SetFileTypeToASCII();
  polyWriter->SetFileName("levelset.poly");
  polyWriter->Write();
  polyWriter->Delete();

  /* Great. We have now executed automatic segmentation and exported the leve set
   * as a mesh. What's left is to exit the automatic segmentation mode and to 
   * merge the segmentation results with the multi-label segmentation image 
   * stored in IRISImageData. The following method will do that for us. */
  application.UpdateIRISWithSnapImageData();
  application.SetCurrentImageDataToIRIS();

  /* Since we are done with the segmentation data, we should release resources
   * associated with it */
  application.ReleaseSNAPImageData();

  /* =======================================================================
   * Section 4. Additional Functionality
   * ====================================================================== */
                                      
  /* One of the features of SNAP it to be able to divide segmentations into two
   * labels using a cut-plane. This can be done programatically, using the
   * following code. In our example, this will divide the left and the right
   * caudates, giving them different labels */

  /* We will paint with label 9 over label 8 */
  application.GetGlobalState()->SetDrawingColorLabel(9);  
  application.GetGlobalState()->SetCoverageMode(PAINT_OVER_COLORS);
  application.GetGlobalState()->SetOverWriteColorLabel(8);  

  /* This actually performs the cut, given an normal vector and intercept that
   * define the cut plane */
  application.RelabelSegmentationWithCutPlane(Vector3d(1,0,0), 64);

  /* We can now save the segmentation result as a 3d image */
  SaveImageToFile("result.gipl",irisData->GetSegmentation()->GetImage());

  /* Thank you for reading this tutorial! */
  return 0;
}

// A global variable required for linkage
std::ostream &verbose = std::cout;