4 #include <vtkAppendPolyData.h>
5 #include <vtkPolyDataWriter.h>
6 #include <vtkCellArray.h>
9 #include "clitkMeshToBinaryImageFilter.h"
12 #include <itkImageDuplicator.h>
14 //--------------------------------------------------------------------
15 template <class ImageType>
17 clitk::ExtractLymphStationsFilter<ImageType>::
18 ExtractStation_2RL_SetDefaultValues()
20 SetFuzzyThreshold("2RL", "CommonCarotidArtery", 0.7);
21 SetFuzzyThreshold("2RL", "BrachioCephalicArtery", 0.7);
22 SetFuzzyThreshold("2RL", "BrachioCephalicVein", 0.3);
23 SetFuzzyThreshold("2RL", "Aorta", 0.7);
24 SetFuzzyThreshold("2RL", "SubclavianArteryRight", 0.5);
25 SetFuzzyThreshold("2RL", "SubclavianArteryLeft", 0.8);
27 //--------------------------------------------------------------------
30 //--------------------------------------------------------------------
31 template <class TImageType>
33 clitk::ExtractLymphStationsFilter<TImageType>::
36 if (CheckForStation("2RL")) {
37 ExtractStation_2RL_SI_Limits();
38 ExtractStation_2RL_Post_Limits();
40 ExtractStation_2RL_Ant_Limits2();
41 //ExtractStation_2RL_Ant_Limits();
43 ExtractStation_2RL_LR_Limits();
44 ExtractStation_2RL_Remove_Structures();
45 ExtractStation_2RL_SeparateRL();
47 // Store image filenames into AFDB
48 writeImage<MaskImageType>(m_ListOfStations["2R"], "seg/Station2R.mhd");
49 writeImage<MaskImageType>(m_ListOfStations["2L"], "seg/Station2L.mhd");
50 GetAFDB()->SetImageFilename("Station2R", "seg/Station2R.mhd");
51 GetAFDB()->SetImageFilename("Station2L", "seg/Station2L.mhd");
55 //--------------------------------------------------------------------
58 //--------------------------------------------------------------------
59 template <class ImageType>
61 clitk::ExtractLymphStationsFilter<ImageType>::
62 ExtractStation_2RL_SI_Limits()
64 // Apex of the chest or Sternum & Carina.
65 StartNewStep("[Station 2RL] Inf/Sup limits with Sternum and TopOfAorticArch/CaudalMarginOfLeftBrachiocephalicVein");
67 /* Rod says: "For the inferior border, unlike in Atlas – UM, there
68 is now a difference between 2R and 2L. 2R stops at the
69 intersection of the caudal margin of the innominate vein with the
70 trachea. 2L extends less inferiorly to the superior border of the
73 /* Get TopOfAorticArch and CaudalMarginOfLeftBrachiocephalicVein
74 - TopOfAorticArch -> can be obtain from Aorta -> most sup part.
75 - CaudalMarginOfLeftBrachiocephalicVein -> must inf part of BrachicephalicVein
77 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
78 MaskImagePointType p = Aorta->GetOrigin(); // initialise to avoid warning
79 clitk::FindExtremaPointInAGivenDirection<MaskImageType>(Aorta, GetBackgroundValue(), 2, false, p);
80 double TopOfAorticArchZ=p[2];
81 GetAFDB()->SetDouble("TopOfAorticArchZ", TopOfAorticArchZ);
83 MaskImagePointer BrachioCephalicVein = GetAFDB()->template GetImage<MaskImageType>("BrachioCephalicVein");
84 clitk::FindExtremaPointInAGivenDirection<MaskImageType>(BrachioCephalicVein, GetBackgroundValue(), 2, true, p);
85 double CaudalMarginOfLeftBrachiocephalicVeinZ=p[2];
86 GetAFDB()->SetDouble("CaudalMarginOfLeftBrachiocephalicVeinZ", CaudalMarginOfLeftBrachiocephalicVeinZ);
88 // First, cut on the most inferior part. Add one slice because this
89 // inf slice should not be included.
90 double inf = std::min(CaudalMarginOfLeftBrachiocephalicVeinZ, TopOfAorticArchZ) + m_Working_Support->GetSpacing()[2];
92 // Get Sternum and search for the upper position
93 MaskImagePointer Sternum = GetAFDB()->template GetImage<MaskImageType>("Sternum");
95 // Search most sup point
96 clitk::FindExtremaPointInAGivenDirection<MaskImageType>(Sternum, GetBackgroundValue(), 2, false, p);
97 double m_SternumZ = p[2];
98 // +Sternum->GetSpacing()[2]; // One more slice, because it is below this point // NOT HERE ?? WHY DIFFERENT FROM 3A ??
102 clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
103 inf, m_SternumZ, true,
104 GetBackgroundValue());
106 StopCurrentStep<MaskImageType>(m_Working_Support);
107 m_ListOfStations["2R"] = m_Working_Support;
108 m_ListOfStations["2L"] = m_Working_Support;
110 //--------------------------------------------------------------------
113 //--------------------------------------------------------------------
114 template <class ImageType>
116 clitk::ExtractLymphStationsFilter<ImageType>::
117 ExtractStation_2RL_Ant_Limits()
119 // -----------------------------------------------------
120 /* Rod says: "The anterior border, as with the Atlas – UM, is
121 posterior to the vessels (right subclavian vein, left
122 brachiocephalic vein, right brachiocephalic vein, left subclavian
123 artery, left common carotid artery and brachiocephalic trunk).
124 These vessels are not included in the nodal station. The anterior
125 border is drawn to the midpoint of the vessel and an imaginary
126 line joins the middle of these vessels. Between the vessels,
127 station 2 is in contact with station 3a." */
129 // -----------------------------------------------------
130 StartNewStep("[Station 2RL] Ant limits with CommonCarotidArtery");
132 // Get CommonCarotidArtery
133 MaskImagePointer CommonCarotidArtery = GetAFDB()->template GetImage<MaskImageType>("CommonCarotidArtery");
135 // Remove Ant to CommonCarotidArtery
136 typedef SliceBySliceRelativePositionFilter<MaskImageType> SliceRelPosFilterType;
137 typename SliceRelPosFilterType::Pointer sliceRelPosFilter = SliceRelPosFilterType::New();
138 sliceRelPosFilter->VerboseStepFlagOff();
139 sliceRelPosFilter->WriteStepFlagOff();
140 sliceRelPosFilter->SetInput(m_Working_Support);
141 sliceRelPosFilter->SetInputObject(CommonCarotidArtery);
142 sliceRelPosFilter->SetDirection(2);
143 sliceRelPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("2RL", "CommonCarotidArtery"));
144 sliceRelPosFilter->AddOrientationTypeString("NotAntTo");
145 sliceRelPosFilter->IntermediateSpacingFlagOn();
146 sliceRelPosFilter->SetIntermediateSpacing(2);
147 sliceRelPosFilter->UniqueConnectedComponentBySliceOff();
148 sliceRelPosFilter->UseASingleObjectConnectedComponentBySliceFlagOff();
149 sliceRelPosFilter->AutoCropFlagOn();
150 sliceRelPosFilter->IgnoreEmptySliceObjectFlagOn();
151 sliceRelPosFilter->RemoveObjectFlagOff();
152 sliceRelPosFilter->Update();
153 m_Working_Support = sliceRelPosFilter->GetOutput();
156 StopCurrentStep<MaskImageType>(m_Working_Support);
157 m_ListOfStations["2R"] = m_Working_Support;
158 m_ListOfStations["2L"] = m_Working_Support;
160 // -----------------------------------------------------
161 // Remove Ant to H line from the Ant most part of the
162 // CommonCarotidArtery until we reach the first slice of
163 // BrachioCephalicArtery
164 StartNewStep("[Station 2RL] Ant limits with CommonCarotidArtery, H line");
166 // First, find the first slice of BrachioCephalicArtery
167 MaskImagePointer BrachioCephalicArtery = GetAFDB()->template GetImage<MaskImageType>("BrachioCephalicArtery");
168 MaskImagePointType p = BrachioCephalicArtery->GetOrigin(); // initialise to avoid warning
169 clitk::FindExtremaPointInAGivenDirection<MaskImageType>(BrachioCephalicArtery, GetBackgroundValue(), 2, false, p);
170 double TopOfBrachioCephalicArteryZ=p[2] + BrachioCephalicArtery->GetSpacing()[2]; // Add one slice
172 // Remove CommonCarotidArtery below this point
173 CommonCarotidArtery = clitk::CropImageRemoveLowerThan<MaskImageType>(CommonCarotidArtery, 2, TopOfBrachioCephalicArteryZ, true, GetBackgroundValue());
175 // Find most Ant points
176 std::vector<MaskImagePointType> ccaAntPositionA;
177 std::vector<MaskImagePointType> ccaAntPositionB;
178 clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition<MaskImageType>(CommonCarotidArtery,
179 GetBackgroundValue(), 2,
181 0, // Horizontal line
185 // Cut ant to this line
186 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
189 GetBackgroundValue(), 1, 10);
192 StopCurrentStep<MaskImageType>(m_Working_Support);
193 m_ListOfStations["2R"] = m_Working_Support;
194 m_ListOfStations["2L"] = m_Working_Support;
196 // -----------------------------------------------------
197 // Ant limit with the BrachioCephalicArtery
198 StartNewStep("[Station 2RL] Ant limits with BrachioCephalicArtery line");
200 // Remove Ant to BrachioCephalicArtery
202 clitk::SliceBySliceRelativePosition<MaskImageType>(m_Working_Support, BrachioCephalicArtery, 2,
203 GetFuzzyThreshold("2RL", "BrachioCephalicArtery"), "NotAntTo", false, 2, true, false);
205 StopCurrentStep<MaskImageType>(m_Working_Support);
206 m_ListOfStations["2R"] = m_Working_Support;
207 m_ListOfStations["2L"] = m_Working_Support;
209 // -----------------------------------------------------
210 // Ant limit with the BrachioCephalicArtery H line
211 StartNewStep("[Station 2RL] Ant limits with BrachioCephalicArtery, Horizontal line");
213 // Find most Ant points
214 std::vector<MaskImagePointType> bctAntPositionA;
215 std::vector<MaskImagePointType> bctAntPositionB;
216 clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition<MaskImageType>(BrachioCephalicArtery,
217 GetBackgroundValue(), 2,
219 0, // Horizontal line
223 // Cut ant to this line
224 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
227 GetBackgroundValue(), 1, 10);
229 StopCurrentStep<MaskImageType>(m_Working_Support);
230 m_ListOfStations["2R"] = m_Working_Support;
231 m_ListOfStations["2L"] = m_Working_Support;
233 // -----------------------------------------------------
234 // Ant limit with the BrachioCephalicVein
235 StartNewStep("[Station 2RL] Ant limits with BrachioCephalicVein");
237 // Remove Ant to BrachioCephalicVein
238 MaskImagePointer BrachioCephalicVein = GetAFDB()->template GetImage<MaskImageType>("BrachioCephalicVein");
240 clitk::SliceBySliceRelativePosition<MaskImageType>(m_Working_Support, BrachioCephalicVein, 2,
241 GetFuzzyThreshold("2RL", "BrachioCephalicVein"), "NotAntTo", false, 2, true, false);
243 StopCurrentStep<MaskImageType>(m_Working_Support);
244 m_ListOfStations["2R"] = m_Working_Support;
245 m_ListOfStations["2L"] = m_Working_Support;
247 //--------------------------------------------------------------------
250 //--------------------------------------------------------------------
251 template <class ImageType>
253 clitk::ExtractLymphStationsFilter<ImageType>::
254 ExtractStation_2RL_Ant_Limits2()
256 // -----------------------------------------------------
257 StartNewStep("[Station 2RL] Ant limits with vessels centroids");
259 // WARNING, as I used "And" after, empty slice in binarizedContour
260 // lead to remove part of the support, although we want to keep
261 // unchanged. So we decide to ResizeImageLike but pad with
262 // ForegroundValue instead of BG
264 // Get or compute the binary mask that separate Ant/Post part
265 // according to vessels
266 MaskImagePointer binarizedContour = FindAntPostVessels2();
267 binarizedContour = clitk::ResizeImageLike<MaskImageType>(binarizedContour,
269 GetForegroundValue());
271 // remove from support
272 typedef clitk::BooleanOperatorLabelImageFilter<MaskImageType> BoolFilterType;
273 typename BoolFilterType::Pointer boolFilter = BoolFilterType::New();
274 boolFilter->InPlaceOn();
275 boolFilter->SetInput1(m_Working_Support);
276 boolFilter->SetInput2(binarizedContour);
277 boolFilter->SetBackgroundValue1(GetBackgroundValue());
278 boolFilter->SetBackgroundValue2(GetBackgroundValue());
279 boolFilter->SetOperationType(BoolFilterType::And);
280 boolFilter->Update();
281 m_Working_Support = boolFilter->GetOutput();
284 StopCurrentStep<MaskImageType>(m_Working_Support);
285 m_ListOfStations["2R"] = m_Working_Support;
286 m_ListOfStations["2L"] = m_Working_Support;
288 //--------------------------------------------------------------------
291 //--------------------------------------------------------------------
292 template <class ImageType>
294 clitk::ExtractLymphStationsFilter<ImageType>::
295 ExtractStation_2RL_Post_Limits()
297 StartNewStep("[Station 2RL] Post limits with post wall of Trachea");
300 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
302 // Resize like the current support (to have the same number of slices)
303 Trachea = clitk::ResizeImageLike<MaskImageType>(Trachea, m_Working_Support, GetBackgroundValue());
305 // Find extrema post positions
306 std::vector<MaskImagePointType> tracheaPostPositionsA;
307 std::vector<MaskImagePointType> tracheaPostPositionsB;
308 clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition<MaskImageType>(Trachea,
309 GetBackgroundValue(), 2,
311 0, // Horizontal line
313 tracheaPostPositionsA,
314 tracheaPostPositionsB);
315 // Cut post to this line
316 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
317 tracheaPostPositionsA,
318 tracheaPostPositionsB,
319 GetBackgroundValue(), 1, -10);
321 StopCurrentStep<MaskImageType>(m_Working_Support);
322 m_ListOfStations["2R"] = m_Working_Support;
323 m_ListOfStations["2L"] = m_Working_Support;
325 //--------------------------------------------------------------------
328 //--------------------------------------------------------------------
329 // Build a vtk mesh from a list of slice number/closed-contours
330 template <class ImageType>
331 vtkSmartPointer<vtkPolyData>
332 clitk::ExtractLymphStationsFilter<ImageType>::
333 Build3DMeshFrom2DContour(const std::vector<ImagePointType> & points)
335 // create a contour, polydata.
336 vtkSmartPointer<vtkPolyData> mesh = vtkSmartPointer<vtkPolyData>::New();
337 mesh->Allocate(); //for cell structures
338 mesh->SetPoints(vtkPoints::New());
340 int point_number = points.size();
341 for (unsigned int i=0; i<points.size(); i++) {
342 mesh->GetPoints()->InsertNextPoint(points[i][0],points[i][1],points[i][2]);
344 ids[1]=(ids[0]+1)%point_number; //0-1,1-2,...,n-1-0
345 mesh->GetLines()->InsertNextCell(2,ids);
350 //--------------------------------------------------------------------
353 //--------------------------------------------------------------------
354 template <class ImageType>
356 clitk::ExtractLymphStationsFilter<ImageType>::
357 ExtractStation_2RL_LR_Limits()
359 // ---------------------------------------------------------------------------
360 StartNewStep("[Station 2RL] Left/Right limits with Aorta");
361 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
362 // DD(GetFuzzyThreshold("2RL", "BrachioCephalicVein"));
364 clitk::SliceBySliceRelativePosition<MaskImageType>(m_Working_Support, Aorta, 2,
365 GetFuzzyThreshold("2RL", "Aorta"),
366 "RightTo", false, 2, true, false);
368 StopCurrentStep<MaskImageType>(m_Working_Support);
369 m_ListOfStations["2R"] = m_Working_Support;
370 m_ListOfStations["2L"] = m_Working_Support;
372 // ---------------------------------------------------------------------------
373 StartNewStep("[Station 2RL] Left/Right limits with SubclavianArtery (Right)");
375 // SliceBySliceRelativePosition + select CCL most at Right
376 MaskImagePointer SubclavianArtery = GetAFDB()->template GetImage<MaskImageType>("SubclavianArtery");
377 typedef SliceBySliceRelativePositionFilter<MaskImageType> SliceRelPosFilterType;
378 typename SliceRelPosFilterType::Pointer sliceRelPosFilter = SliceRelPosFilterType::New();
379 sliceRelPosFilter->VerboseStepFlagOff();
380 sliceRelPosFilter->WriteStepFlagOff();
381 sliceRelPosFilter->SetInput(m_Working_Support);
382 sliceRelPosFilter->SetInputObject(SubclavianArtery);
383 sliceRelPosFilter->SetDirection(2);
384 sliceRelPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("2RL", "SubclavianArteryRight"));
385 sliceRelPosFilter->AddOrientationTypeString("NotRightTo");
386 sliceRelPosFilter->IntermediateSpacingFlagOn();
387 sliceRelPosFilter->SetIntermediateSpacing(2);
388 sliceRelPosFilter->UniqueConnectedComponentBySliceOff();
389 sliceRelPosFilter->UseASingleObjectConnectedComponentBySliceFlagOff();
391 sliceRelPosFilter->CCLSelectionFlagOn(); // select one CCL by slice
392 sliceRelPosFilter->SetCCLSelectionDimension(0); // select according to X (0) axis
393 sliceRelPosFilter->SetCCLSelectionDirection(-1); // select most at Right
394 sliceRelPosFilter->CCLSelectionIgnoreSingleCCLFlagOn(); // ignore if only one CCL
396 sliceRelPosFilter->AutoCropFlagOn();
397 sliceRelPosFilter->IgnoreEmptySliceObjectFlagOn();
398 sliceRelPosFilter->RemoveObjectFlagOff();
399 sliceRelPosFilter->Update();
400 m_Working_Support = sliceRelPosFilter->GetOutput();
403 StopCurrentStep<MaskImageType>(m_Working_Support);
404 m_ListOfStations["2R"] = m_Working_Support;
405 m_ListOfStations["2L"] = m_Working_Support;
408 // ---------------------------------------------------------------------------
409 StartNewStep("[Station 2RL] Left/Right limits with SubclavianArtery (Left)");
411 // SliceBySliceRelativePosition + select CCL most at Right
412 sliceRelPosFilter = SliceRelPosFilterType::New();
413 sliceRelPosFilter->VerboseStepFlagOff();
414 sliceRelPosFilter->WriteStepFlagOff();
415 sliceRelPosFilter->SetInput(m_Working_Support);
416 sliceRelPosFilter->SetInputObject(SubclavianArtery);
417 sliceRelPosFilter->SetDirection(2);
418 sliceRelPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("2RL", "SubclavianArteryLeft"));
419 sliceRelPosFilter->AddOrientationTypeString("NotLeftTo");
420 sliceRelPosFilter->IntermediateSpacingFlagOn();
421 sliceRelPosFilter->SetIntermediateSpacing(2);
422 sliceRelPosFilter->UniqueConnectedComponentBySliceOff();
423 sliceRelPosFilter->UseASingleObjectConnectedComponentBySliceFlagOff();
425 sliceRelPosFilter->CCLSelectionFlagOn(); // select one CCL by slice
426 sliceRelPosFilter->SetCCLSelectionDimension(0); // select according to X (0) axis
427 sliceRelPosFilter->SetCCLSelectionDirection(+1); // select most at Left
428 sliceRelPosFilter->CCLSelectionIgnoreSingleCCLFlagOff(); // do not ignore if only one CCL
430 sliceRelPosFilter->AutoCropFlagOn();
431 sliceRelPosFilter->IgnoreEmptySliceObjectFlagOn();
432 sliceRelPosFilter->RemoveObjectFlagOff();
433 sliceRelPosFilter->Update();
434 m_Working_Support = sliceRelPosFilter->GetOutput();
437 StopCurrentStep<MaskImageType>(m_Working_Support);
438 m_ListOfStations["2R"] = m_Working_Support;
439 m_ListOfStations["2L"] = m_Working_Support;
441 //--------------------------------------------------------------------
443 //--------------------------------------------------------------------
444 template <class ImageType>
446 clitk::ExtractLymphStationsFilter<ImageType>::
447 ExtractStation_2RL_Remove_Structures()
449 Remove_Structures("2RL", "BrachioCephalicVein");
450 Remove_Structures("2RL", "CommonCarotidArtery");
451 Remove_Structures("2RL", "SubclavianArtery");
452 Remove_Structures("2RL", "Thyroid");
453 Remove_Structures("2RL", "Aorta");
456 StopCurrentStep<MaskImageType>(m_Working_Support);
457 m_ListOfStations["2R"] = m_Working_Support;
458 m_ListOfStations["2L"] = m_Working_Support;
460 //--------------------------------------------------------------------
463 //--------------------------------------------------------------------
464 template <class ImageType>
466 clitk::ExtractLymphStationsFilter<ImageType>::
467 ExtractStation_2RL_SeparateRL()
469 // ---------------------------------------------------------------------------
470 StartNewStep("[Station 2RL] Separate 2R/2L according to Trachea");
474 "For station 2 there is a shift, dividing 2R from 2L, from midline
475 to the left paratracheal border."
478 - Consider Trachea SliceBySlice
479 - find extrema at Left
480 - add margins towards Right
481 - remove what is at Left of this line
485 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
487 // Resize like the current support (to have the same number of slices)
488 Trachea = clitk::ResizeImageLike<MaskImageType>(Trachea, m_Working_Support, GetBackgroundValue());
490 // Find extrema post positions
491 std::vector<MaskImagePointType> tracheaLeftPositionsA;
492 std::vector<MaskImagePointType> tracheaLeftPositionsB;
493 clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition<MaskImageType>(Trachea,
494 GetBackgroundValue(), 2,
498 tracheaLeftPositionsA,
499 tracheaLeftPositionsB);
500 // Copy support for R and L
501 typedef itk::ImageDuplicator<MaskImageType> DuplicatorType;
502 DuplicatorType::Pointer duplicator = DuplicatorType::New();
503 duplicator->SetInputImage(m_Working_Support);
504 duplicator->Update();
505 MaskImageType::Pointer m_Working_Support2 = duplicator->GetOutput();
507 // Cut post to this line for Right part
508 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
509 tracheaLeftPositionsA,
510 tracheaLeftPositionsB,
511 GetBackgroundValue(), 0, -10);
512 writeImage<MaskImageType>(m_Working_Support, "R.mhd");
514 // Cut post to this line for Left part
515 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support2,
516 tracheaLeftPositionsA,
517 tracheaLeftPositionsB,
518 GetBackgroundValue(), 0, +10);
519 writeImage<MaskImageType>(m_Working_Support2, "L.mhd");
522 StopCurrentStep<MaskImageType>(m_Working_Support);
523 m_ListOfStations["2R"] = m_Working_Support;
524 m_ListOfStations["2L"] = m_Working_Support2;
526 //--------------------------------------------------------------------