2 /*=========================================================================
3 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
6 - University of LYON http://www.universite-lyon.fr/
7 - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr
8 - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the copyright notices for more information.
14 It is distributed under dual licence
16 - BSD See included LICENSE.txt file
17 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
18 ======================================================================-====*/
21 #include "clitkTestFilter_ggo.h"
22 #include "clitkImageCommon.h"
23 #include "clitkBooleanOperatorLabelImageFilter.h"
24 #include "clitkAutoCropFilter.h"
25 //#include "clitkRelativePositionConstraintLabelImageFilter.h"
26 #include "clitkResampleImageWithOptionsFilter.h"
27 #include "clitkAddRelativePositionConstraintToLabelImageFilter.h"
28 #include "clitkExtractLungFilter.h"
29 #include "clitkExtractPatientFilter.h"
30 #include "clitkExtractMediastinumFilter.h"
31 //#include "clitkTestStation7.h"
32 #include "clitkSegmentationUtils.h"
33 #include "clitkMorphoMathFilter.h"
36 #include "RelativePositionPropImageFilter.h"
39 #include <vtkSmartPointer.h>
40 #include <vtkPolyData.h>
41 #include <vtkCellArray.h>
42 #include <vtkPolyDataWriter.h>
43 #include <vtkImageData.h>
44 #include <vtkMetaImageWriter.h>
45 #include <vtkPolyDataToImageStencil.h>
46 #include <vtkLinearExtrusionFilter.h>
47 #include <vtkImageStencil.h>
52 template<class PointType>
53 class comparePointsX {
55 bool operator() (PointType i, PointType j) { return (i[0]<j[0]); }
59 void HypercubeCorners(std::vector<itk::Point<double, Dim> > & out) {
60 std::vector<itk::Point<double, Dim-1> > previous;
61 HypercubeCorners<Dim-1>(previous);
62 out.resize(previous.size()*2);
63 for(uint i=0; i<out.size(); i++) {
64 itk::Point<double, Dim> p;
65 if (i<previous.size()) p[0] = 0;
67 for(int j=0; j<Dim; j++) p[j+1] = previous[i%previous.size()][j]; //NON i p
73 void HypercubeCorners<1>(std::vector<itk::Point<double, 1> > & out) {
80 //--------------------------------------------------------------------
81 int main(int argc, char * argv[]) {
84 GGO(clitkTestFilter, args_info);
87 //typedef unsigned char PixelType;
88 typedef short PixelType;
89 static const int Dim=3;
90 typedef itk::Image<PixelType, Dim> InputImageType;
93 InputImageType::Pointer input1;
94 InputImageType::Pointer input2;
95 InputImageType::Pointer input3;
96 if (args_info.input1_given) input1 = clitk::readImage<InputImageType>(args_info.input1_arg, true);
97 if (args_info.input2_given) input2 = clitk::readImage<InputImageType>(args_info.input2_arg, true);
98 if (args_info.input3_given) input3 = clitk::readImage<InputImageType>(args_info.input3_arg, true);
101 InputImageType::Pointer output;
103 //--------------------------------------------------------------------
104 // Filter test BooleanOperatorLabelImageFilter
106 typedef clitk::BooleanOperatorLabelImageFilter<InputImageType> FilterType;
107 FilterType::Pointer filter = FilterType::New();
108 filter->SetInput1(input1);
109 filter->SetInput2(input2);
110 output = clitk::NewImageLike<InputImageType>(input1);
111 filter->GraftOutput(output); /// TO VERIFY !!!
113 filter->SetInput2(input3);
115 output = filter->GetOutput();
116 clitk::writeImage<InputImageType>(output, args_info.output_arg);
119 //--------------------------------------------------------------------
120 // Filter test AutoCropLabelImageFilter
122 typedef clitk::AutoCropFilter<InputImageType> FilterType;
123 FilterType::Pointer filter = FilterType::New();
124 filter->SetInput(input1);
126 output = filter->GetOutput();
127 clitk::writeImage<InputImageType>(output, args_info.output_arg);
130 //--------------------------------------------------------------------
131 // Filter test RelativePositionPropImageFilter
133 typedef itk::Image<float, Dim> OutputImageType;
134 OutputImageType::Pointer outputf;
135 typedef itk::RelativePositionPropImageFilter<InputImageType, OutputImageType> FilterType;
136 FilterType::Pointer filter = FilterType::New();
137 filter->SetInput(input1);
139 filter->SetAlpha1(clitk::deg2rad(args_info.angle1_arg)); // xy plane
140 filter->SetAlpha2(clitk::deg2rad(args_info.angle2_arg));
141 filter->SetK1(M_PI/2.0); // Opening parameter, default = pi/2
142 filter->SetFast(true);
143 filter->SetRadius(2);
144 filter->SetVerboseProgress(true);
156 clitk::writeImage<OutputImageType>(filter->GetOutput(), args_info.output_arg);
159 //--------------------------------------------------------------------
162 typedef itk::Image<short, Dim> OutputImageType;
163 typedef clitk::ResampleImageWithOptionsFilter<InputImageType, OutputImageType> FilterType;
164 FilterType::Pointer filter = FilterType::New();
165 filter->SetInput(input1);
166 filter->SetOutputIsoSpacing(1);
167 //filter->SetNumberOfThreads(4); // auto
168 filter->SetGaussianFilteringEnabled(false);
170 clitk::writeImage<OutputImageType>(filter->GetOutput(), args_info.output_arg);
173 //--------------------------------------------------------------------
174 // Filter AddRelativePositionConstraintToLabelImageFilter test
177 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<InputImageType> FilterType;
178 FilterType::Pointer filter = FilterType::New();
179 filter->SetInput(input1);
180 filter->SetInputObject(input2);
181 filter->SetOrientationType(FilterType::LeftTo);
182 filter->SetIntermediateSpacing(5);
183 filter->SetFuzzyThreshold(0.5);
184 filter->VerboseStepOn();
185 filter->WriteStepOff();
188 filter->SetInput(filter->GetOutput());
189 filter->SetInputObject(input2);
190 filter->SetOrientationType(FilterType::RightTo);
191 filter->SetIntermediateSpacing(5);
192 filter->SetFuzzyThreshold(0.5);
195 clitk::writeImage<InputImageType>(filter->GetOutput(), args_info.output_arg);
199 //--------------------------------------------------------------------
200 // Filter test ExtractPatientFilter
203 typedef itk::Image<char, Dim> OutputImageType;
204 typedef clitk::ExtractPatientFilter<InputImageType, OutputImageType> FilterType;
205 FilterType::Pointer filter = FilterType::New();
206 filter->SetInput(input1);
207 filter->VerboseStepOn();
208 filter->WriteStepOn();
210 filter->SetUpperThreshold(-300);
211 filter->FinalOpenCloseOff(); // default=on (not rezally needed ?)
213 OutputImageType::Pointer output = filter->GetOutput();
214 clitk::writeImage<OutputImageType>(output, args_info.output_arg);
218 //--------------------------------------------------------------------
219 // Filter test ExtractLungsFilter
222 typedef itk::Image<PixelType, Dim> OutputImageType; // to change into char
223 typedef clitk::ExtractLungFilter<InputImageType, OutputImageType> FilterType;
224 FilterType::Pointer filter = FilterType::New();
225 // DD(filter->GetNumberOfSteps());
226 filter->SetInput(input1); // CT
227 filter->SetInputPatientMask(input2, 0); // Patient mask and BG value
228 filter->VerboseStepOn();
229 filter->WriteStepOn();
231 //filter->SetBackgroundValue(0);
232 filter->SetUpperThreshold(-300);
233 // filter->SetMinimumComponentSize(100);
236 OutputImageType::Pointer output = filter->GetOutput();
237 clitk::writeImage<OutputImageType>(output, args_info.output_arg);
241 //--------------------------------------------------------------------
242 // Filter test ExtractMediastinumFilter
245 typedef clitk::ExtractMediastinumFilter<InputImageType> FilterType;
246 FilterType::Pointer filter = FilterType::New();
247 filter->SetInputPatientLabelImage(input1);
248 filter->SetInputLungLabelImage(input2, 0, 1, 2); // BG, FG Left Lung, FG Right Lung
249 filter->SetInputBonesLabelImage(input3);
250 filter->VerboseStepOn();
251 filter->WriteStepOn();
253 output = filter->GetOutput();
254 clitk::writeImage<InputImageType>(output, args_info.output_arg);
258 //--------------------------------------------------------------------
259 // Test for auto register sub-task in a segmentation process
261 // ExtractLymphStation_7 * s7 = new ExtractLymphStation_7;
262 // s7->SetArgsInfo<args_info_clitkTestFilter>(args_info);
263 // GetParent->SetArgsInfo<>
264 //s7->StartSegmentation();
267 //--------------------------------------------------------------------
268 // Test for biinary image from a contour set
273 typedef itk::Image<InputImageType::PixelType, InputImageType::ImageDimension-1> SliceType;
275 // Build the list of slices
276 std::vector<SliceType::Pointer> slices;
277 clitk::ExtractSlices<InputImageType>(input1, 2, slices);
280 // HOW TO DO SEVERAL BY SLICE !!! not a map ?
282 // Compute centroids 3D centroids by slices
284 std::map<int, std::vector<InputImageType::PointType> > centroids3D;
285 for(uint i=0; i<slices.size(); i++) {
287 slices[i] = clitk::Labelize<SliceType>(slices[i], BG, true, 1);
289 std::vector<SliceType::PointType> temp;
290 clitk::ComputeCentroids<SliceType>(slices[i], BG, temp);
291 for(uint j=1; j<temp.size(); j++) {
292 InputImageType::PointType a;
293 clitk::PointsUtils<InputImageType>::Convert2DTo3D(temp[j], input1, i, a);
294 centroids3D[i].push_back(a);
298 // REPRENDRE POUR TOUT FAIRE BY SLICE (pas de i);
300 // Take a given slice i=29
302 SliceType::Pointer slice = slices[index];
303 std::vector<InputImageType::PointType> points = centroids3D[index];
305 // Sort points from R to L
306 std::sort(points.begin(), points.end(),
307 comparePointsX<InputImageType::PointType>());
309 // Slice corners (quel sens ?)
311 // Compute min and max coordinates
313 typedef InputImageType::PointType PointType;
314 typedef InputImageType::IndexType IndexType;
315 PointType min_c, max_c;
316 IndexType min_i, max_i;
317 min_i = input1->GetLargestPossibleRegion().GetIndex();
318 for(uint i=0; i<dim; i++) max_i[i] = input1->GetLargestPossibleRegion().GetSize()[i] + min_i[i];
319 input1->TransformIndexToPhysicalPoint(min_i, min_c);
320 input1->TransformIndexToPhysicalPoint(max_i, max_c);
322 // Compute the corners coordinates
323 std::vector<itk::Point<double, dim> > l;
324 HypercubeCorners<dim>(l);
325 for(uint i=0; i<l.size(); i++) {
326 for(uint j=0; j<dim; j++) {
327 if (l[i][j] == 0) l[i][j] = min_c[j];
328 if (l[i][j] == 1) l[i][j] = max_c[j];
334 // Add first/last point, horizontally to the image boundaries
336 double sz = points[0][2]; // slice Z
337 double margin = 2; // needed (if not forget to remove the first line)
339 InputImageType::PointType p = min_c;
340 p[0] -= margin; // margins
341 p[1] -= margin; // margins
343 points.insert(points.begin(), p);
346 p[0] = min_c[0] - margin; //margin
347 points.insert(points.begin(), p);
354 p[1] = min_c[1]-margin;
361 DDV(points, points.size());
363 // create a contour, polydata.
364 vtkSmartPointer<vtkPolyData> mesh = vtkSmartPointer<vtkPolyData>::New();
365 mesh->Allocate(); //for cell structures
366 mesh->SetPoints(vtkPoints::New());
368 int point_number = points.size();
369 for (unsigned int i=0; i<points.size(); i++) {
370 mesh->GetPoints()->InsertNextPoint(points[i][0],points[i][1],points[i][2]);
372 ids[1]=(ids[0]+1)%point_number; //0-1,1-2,...,n-1-0
373 mesh->GetLines()->InsertNextCell(2,ids);
376 vtkSmartPointer<vtkPolyDataWriter> w = vtkSmartPointer<vtkPolyDataWriter>::New();
378 w->SetFileName("bidon.vtk");
383 double *bounds=mesh->GetBounds();
387 vtkSmartPointer<vtkImageData> binary_image=vtkSmartPointer<vtkImageData>::New();
388 binary_image->SetScalarTypeToUnsignedChar();
389 ///Use the smallest mask in which the mesh fits
390 // Add two voxels on each side to make sure the mesh fits
391 // double * samp_origin=
392 InputImageType::PointType samp_origin = input1->GetOrigin();
394 InputImageType::SpacingType spacing=input1->GetSpacing();
395 double * spacing2 = new double[3];
396 spacing2[0] = spacing[0];
397 spacing2[1] = spacing[1];
398 spacing2[2] = spacing[2];
400 binary_image->SetSpacing(spacing2);
401 /// Put the origin on a voxel to avoid small skips
402 DD(floor((bounds[0]-samp_origin[0])/spacing[0]-2)*spacing[0]);
406 binary_image->SetOrigin(//samp_origin[0], samp_origin[1], samp_origin[2]);
407 floor((bounds[0]-samp_origin[0])/spacing[0]-2)*spacing[0]+samp_origin[0],
408 floor((bounds[2]-samp_origin[1])/spacing[1]-2)*spacing[1]+samp_origin[1],
409 floor((bounds[4]-samp_origin[2])/spacing[2]-2)*spacing[2]+samp_origin[2]);
410 double * origin=binary_image->GetOrigin();
411 binary_image->SetExtent(0,ceil((bounds[1]-origin[0])/spacing[0]+4), // Joel used +4 here (?)
412 0,ceil((bounds[3]-origin[1])/spacing[1]+4),
413 0,ceil((bounds[5]-origin[2])/spacing[2]+4));
414 binary_image->AllocateScalars();
415 memset(binary_image->GetScalarPointer(),0,
416 binary_image->GetDimensions()[0]*binary_image->GetDimensions()[1]*binary_image->GetDimensions()[2]*sizeof(unsigned char));
418 vtkSmartPointer<vtkPolyDataToImageStencil> sts=vtkSmartPointer<vtkPolyDataToImageStencil>::New();
419 //The following line is extremely important
420 //http://www.nabble.com/Bug-in-vtkPolyDataToImageStencil--td23368312.html#a23370933
421 sts->SetTolerance(0);
422 sts->SetInformationInput(binary_image);
426 vtkSmartPointer<vtkLinearExtrusionFilter> extrude=vtkSmartPointer<vtkLinearExtrusionFilter>::New();
427 extrude->SetInput(mesh);
429 /// NO ????We extrude in the -slice_spacing direction to respect the FOCAL convention ???
431 extrude->SetVector(0, 0, input1->GetSpacing()[2]);//slice_spacing); // 2* ? yes use a single one
432 sts->SetInput(extrude->GetOutput());
437 vtkSmartPointer<vtkImageStencil> stencil=vtkSmartPointer<vtkImageStencil>::New();
438 stencil->SetStencil(sts->GetOutput());
439 stencil->SetInput(binary_image);
443 vtkSmartPointer<vtkMetaImageWriter> ww = vtkSmartPointer<vtkMetaImageWriter>::New();
444 ww->SetInput(stencil->GetOutput());
445 ww->SetFileName("binary.mhd");
448 //--------------------------------------------------------------------
450 //--------------------------------------------------------------------
451 // Test for vessels ReconstructionByDilatation
453 // Read input CT (already croped)
455 // erode n times (or in 2D ?)
459 // - for all CCL, ReconstructionByDilatation in initial mask
466 typedef itk::Image<unsigned char, InputImageType::ImageDimension> MaskImageType;
467 typedef itk::BinaryThresholdImageFilter<InputImageType, MaskImageType> BinarizeFilterType;
468 BinarizeFilterType::Pointer binarizeFilter = BinarizeFilterType::New();
469 binarizeFilter->SetInput(input1);
470 binarizeFilter->SetLowerThreshold(150);
471 binarizeFilter->SetInsideValue(FG);
472 binarizeFilter->SetOutsideValue(BG);
473 binarizeFilter->Update();
474 MaskImageType::Pointer mask = binarizeFilter->GetOutput();
475 clitk::writeImage<MaskImageType>(mask, "m.mhd");
478 typedef itk::Image<MaskImageType::PixelType, MaskImageType::ImageDimension-1> SliceType;
479 std::vector<SliceType> slices_mask;
480 clitk::ExtractSlices<MaskImageType>(mask, 2, slices_mask);
481 DD(slices_mask.size());
483 std::vector<SliceType> debug_eroded;
484 std::vector<SliceType> debug_labeled;
487 for(uint i=0; i<slices_mask.size(); i++) {
491 clitk::MorphoMathFilter<SliceType>::Pointer f = clitk::MorphoMathFilter<SliceType>::New();
492 f->SetInput(slices_mask[i]);
493 f->SetBackgroundValue(BG);
494 f->SetForegroundValue(FG);
496 f->SetOperationType(0); // Erode
498 SliceType::Pointer eroded = f->GetOutput();
499 debug_eroded.push_back(eroded);
503 SliceType::Pointer labeled = Labelize<MaskSliceType>(slices_mask[i], 0, false, 10);
504 debug_labeled.push_back(labeled);
506 // ReconstructionByDilatation
511 MaskImageType::Pointer eroded = clitk::JoinSlices<MaskImageType>(debug_eroded, mask, 2);
512 clitk::writeImage<MaskImageType>(eroded, "eroded.mhd");
514 MaskImageType::Pointer labeled = clitk::JoinSlices<MaskImageType>(debug_labeled, mask, 2);
515 clitk::writeImage<MaskImageType>(labeled, "labeled.mhd");
518 //--------------------------------------------------------------------
520 // This is the end my friend
523 //--------------------------------------------------------------------