2 //--------------------------------------------------------------------
3 template <class ImageType>
5 clitk::ExtractLymphStationsFilter<ImageType>::
6 ExtractStation_3P_SetDefaultValues()
9 //--------------------------------------------------------------------
12 //--------------------------------------------------------------------
13 template <class ImageType>
15 clitk::ExtractLymphStationsFilter<ImageType>::
16 ExtractStation_3P_SI_Limits()
19 Apex of the chest & Carina.
21 StartNewStep("[Station 3P] Inf/Sup limits with apex of the chest and carina");
23 // Get Carina position (has been determined in Station8)
24 m_CarinaZ = GetAFDB()->GetDouble("CarinaZ");
26 // Get Apex of the Chest. The "lungs" structure is autocroped so we
27 // consider the most superior point.
28 MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
29 MaskImageIndexType index = Lungs->GetLargestPossibleRegion().GetIndex();
30 index += Lungs->GetLargestPossibleRegion().GetSize();
32 Lungs->TransformIndexToPhysicalPoint(index, p);
33 p[2] -= 5; // 5 mm below because the autocrop is slightly above the apex
34 double m_ApexOfTheChest = p[2];
37 Superior limit = carina
38 Inferior limit = Apex of the chest */
40 clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
42 m_ApexOfTheChest, true,
43 GetBackgroundValue());
45 StopCurrentStep<MaskImageType>(m_Working_Support);
46 m_ListOfStations["3P"] = m_Working_Support;
48 //--------------------------------------------------------------------
51 //--------------------------------------------------------------------
52 template <class ImageType>
54 clitk::ExtractLymphStationsFilter<ImageType>::
55 ExtractStation_3P_Remove_Structures()
58 3 - (sup) remove Aorta, VB, subcl
59 not LR subcl ! -> a séparer LR ?
60 (inf) remove Eso, Aorta, Azygousvein
63 StartNewStep("[Station 3P] Remove some structures.");
65 Remove_Structures("3P", "Aorta");
66 Remove_Structures("3P", "VertebralBody");
67 Remove_Structures("3P", "SubclavianArtery");
68 Remove_Structures("3P", "Esophagus");
69 Remove_Structures("3P", "Azygousvein");
70 Remove_Structures("3P", "Thyroid");
71 Remove_Structures("3P", "VertebralArtery");
73 StopCurrentStep<MaskImageType>(m_Working_Support);
74 m_ListOfStations["3P"] = m_Working_Support;
76 //--------------------------------------------------------------------
79 //--------------------------------------------------------------------
80 template <class ImageType>
82 clitk::ExtractLymphStationsFilter<ImageType>::
83 ExtractStation_3P_Ant_Limits()
88 Anteriorly, it is in contact with the posterior aspect of Stations
89 1–2 superiorly (Fig. 2A–C) and with Stations 4R and 4L inferiorly
90 (Fig. 2D–I and 3A–C). The anterior limit of Station 3P is kept
91 posterior to the trachea, which is defined by an imaginary
92 horizontal line running along the posterior wall of the trachea
93 (Fig. 2B,E, red line). Posteriorly, it is delineated along the
94 anterior and lateral borders of the vertebral body until an
95 imaginary horizontal line running 1 cm posteriorly to the
96 anterior border of the vertebral body (Fig. 2D).
98 1 - post to the trachea : define an imaginary line just post the
99 Trachea and remove what is anterior.
102 StartNewStep("[Station 3P] Ant limits with Trachea ");
105 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
107 // Crop like current support (need by SliceBySliceSetBackgroundFromLineSeparation after)
109 clitk::ResizeImageLike<MaskImageType>(Trachea, m_Working_Support, GetBackgroundValue());
111 // Slice by slice, determine the most post point of the trachea (A)
112 // and consider a second point on the same horizontal line (B)
113 std::vector<MaskImagePointType> p_A;
114 std::vector<MaskImagePointType> p_B;
115 std::vector<typename MaskSliceType::Pointer> slices;
116 clitk::ExtractSlices<MaskImageType>(Trachea, 2, slices);
117 MaskImagePointType p;
118 typename MaskSliceType::PointType sp;
119 for(uint i=0; i<slices.size(); i++) {
120 // First only consider the main CCL (trachea, not bronchus)
121 slices[i] = Labelize<MaskSliceType>(slices[i], 0, true, 100);
122 slices[i] = KeepLabels<MaskSliceType>(slices[i], GetBackgroundValue(),
123 GetForegroundValue(), 1, 1, true);
124 // Find most posterior point
125 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i], GetBackgroundValue(),
127 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp, Trachea, i, p);
132 clitk::WriteListOfLandmarks<MaskImageType>(p_A, "trachea-post.txt");
134 // Remove Ant part above those lines
135 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
137 GetBackgroundValue(),
139 // End, release memory
140 GetAFDB()->template ReleaseImage<MaskImageType>("Trachea");
141 StopCurrentStep<MaskImageType>(m_Working_Support);
142 m_ListOfStations["3P"] = m_Working_Support;
144 //--------------------------------------------------------------------
147 //--------------------------------------------------------------------
148 template <class ImageType>
150 clitk::ExtractLymphStationsFilter<ImageType>::
151 ExtractStation_3P_Post_Limits()
156 Anteriorly, it is in contact with the posterior aspect of Stations
157 1–2 superiorly (Fig. 2A–C) and with Stations 4R and 4L inferiorly
158 (Fig. 2D–I and 3A–C). The anterior limit of Station 3P is kept
159 posterior to the trachea, which is defined by an imaginary
160 horizontal line running along the posterior wall of the trachea
161 (Fig. 2B,E, red line). Posteriorly, it is delineated along the
162 anterior and lateral borders of the vertebral body until an
163 imaginary horizontal line running 1 cm posteriorly to the
164 anterior border of the vertebral body (Fig. 2D).
166 2 - post to the trachea : define an imaginary line just post the
167 Trachea and remove what is anterior.
170 StartNewStep("[Station 3P] Post limits with VertebralBody ");
172 // Load VertebralBody
173 MaskImagePointer VertebralBody = GetAFDB()->template GetImage<MaskImageType>("VertebralBody");
175 // Crop like current support (need by SliceBySliceSetBackgroundFromLineSeparation after)
176 VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
178 writeImage<MaskImageType>(VertebralBody, "vb.mhd");
180 // Compute VertebralBody most Ant position (again because slices
181 // changes). Slice by slice, determine the most post point of the
182 // trachea (A) and consider a second point on the same horizontal
184 std::vector<MaskImagePointType> p_A;
185 std::vector<MaskImagePointType> p_B;
186 std::vector<typename MaskSliceType::Pointer> slices;
187 clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, slices);
188 MaskImagePointType p;
189 typename MaskSliceType::PointType sp;
190 for(uint i=0; i<slices.size(); i++) {
191 // Find most anterior point
192 bool found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i], GetBackgroundValue(),
195 // If the VertebralBody stop superiorly before the end of
196 // m_Working_Support, we consider the same previous point.
199 p[2] += VertebralBody->GetSpacing()[2];
202 p[2] += VertebralBody->GetSpacing()[2];
206 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp, VertebralBody, i, p);
207 p[1] += 10; // Consider 10 mm more post
213 clitk::WriteListOfLandmarks<MaskImageType>(p_A, "vb-ant.txt");
216 // Remove Ant part above those lines
217 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
219 GetBackgroundValue(),
221 writeImage<MaskImageType>(m_Working_Support, "a.mhd");
223 StopCurrentStep<MaskImageType>(m_Working_Support);
224 m_ListOfStations["3P"] = m_Working_Support;
226 //--------------------------------------------------------------------
229 //--------------------------------------------------------------------
230 template <class ImageType>
232 clitk::ExtractLymphStationsFilter<ImageType>::
233 ExtractStation_3P_LR_sup_Limits()
236 "On the right side, the limit is defined by the air–soft-tissue
237 interface. On the left side, it is defined by the air–tissue
238 interface superiorly (Fig. 2A–C) and the aorta inferiorly
239 (Figs. 2D–I and 3A–C)."
242 Resizelike support : Trachea, SubclavianArtery
243 Trachea, slice by slice, get centroid trachea
244 SubclavianArtery, slice by slice, CCL
245 prendre la 1ère à L et R, not at Left
248 StartNewStep("[Station 3P] Left/Right limits (superior part) ");
251 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
252 MaskImagePointer SubclavianArtery = GetAFDB()->template GetImage<MaskImageType>("SubclavianArtery");
254 // Crop like current support
255 Trachea = clitk::ResizeImageLike<MaskImageType>(Trachea, m_Working_Support, GetBackgroundValue());
256 SubclavianArtery = clitk::ResizeImageLike<MaskImageType>(SubclavianArtery, m_Working_Support, GetBackgroundValue());
258 writeImage<MaskImageType>(Trachea, "tr.mhd");
259 writeImage<MaskImageType>(SubclavianArtery, "sca.mhd");
261 // Get list of slices
262 std::vector<MaskSlicePointer> slices_support;
263 std::vector<MaskSlicePointer> slices_trachea;
264 std::vector<MaskSlicePointer> slices_subclavianartery;
265 clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices_support);
266 clitk::ExtractSlices<MaskImageType>(Trachea, 2, slices_trachea);
267 clitk::ExtractSlices<MaskImageType>(SubclavianArtery, 2, slices_subclavianartery);
270 std::vector<MaskImagePointType> points;
271 MaskImagePointType p;
272 for(uint i=0; i<slices_support.size(); i++) {
273 // Get Trachea centroid
274 std::vector<typename MaskSliceType::PointType> centroids;
275 typename MaskSliceType::PointType c;
276 ComputeCentroids<MaskSliceType>(slices_trachea[i], GetBackgroundValue(), centroids);
279 // [debug] Store point
280 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(centroids[1], Trachea, i, p);
283 // Get Right and Left CCL in SubclavianArtery
284 slices_subclavianartery[i] = Labelize<MaskSliceType>(slices_subclavianartery[i], 0, true, 10);
285 ComputeCentroids<MaskSliceType>(slices_subclavianartery[i], GetBackgroundValue(), centroids);
287 if (centroids.size() > 1) {
288 // Determine the one at Right/Left -> first after Trachea
290 typename MaskSliceType::PointType right;
291 typename MaskSliceType::PointType left;
296 for(uint j=1; j<centroids.size(); j++) {
297 if (centroids[j][0] < c[0]) { // At Right of Trachea centroid
298 if (centroids[j][0] >= right[0]) {
299 right = centroids[j];
303 if (centroids[j][0] > c[0]) { // At Left of Trachea centroid
304 if (centroids[j][0] <= left[0]) {
311 if (label_right != -1) {
314 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(centroids[label_right], SubclavianArtery, i, p);
317 // Set Background and ForegroundValue according to label_right
318 MaskSlicePointer object =
319 clitk::Binarize<MaskSliceType>(slices_subclavianartery[i], label_right, label_right,
320 GetBackgroundValue(), GetForegroundValue());
322 // Relative Position : not at Right
323 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskSliceType> RelPosFilterType;
324 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
325 relPosFilter->VerboseStepFlagOff();
326 relPosFilter->WriteStepFlagOff();
327 relPosFilter->SetBackgroundValue(GetBackgroundValue());
328 relPosFilter->SetInput(slices_support[i]);
329 relPosFilter->SetInputObject(object);
330 relPosFilter->AddOrientationTypeString("R");
331 relPosFilter->SetInverseOrientationFlag(true);
332 // relPosFilter->SetIntermediateSpacing(3);
333 relPosFilter->SetIntermediateSpacingFlag(false);
334 relPosFilter->SetFuzzyThreshold(0.7);
335 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
336 relPosFilter->Update();
337 slices_support[i] = relPosFilter->GetOutput();
339 // Relative Position : not Anterior
340 relPosFilter = RelPosFilterType::New();
341 relPosFilter->VerboseStepFlagOff();
342 relPosFilter->WriteStepFlagOff();
343 relPosFilter->SetBackgroundValue(GetBackgroundValue());
344 relPosFilter->SetInput(slices_support[i]);
345 relPosFilter->SetInputObject(object);
346 relPosFilter->AddOrientationTypeString("A");
347 relPosFilter->SetInverseOrientationFlag(true);
348 // relPosFilter->SetIntermediateSpacing(3);
349 relPosFilter->SetIntermediateSpacingFlag(false);
350 relPosFilter->SetFuzzyThreshold(0.7);
351 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
352 relPosFilter->Update();
353 slices_support[i] = relPosFilter->GetOutput();
355 } // End RelativePosition for Right
358 if (label_left != -1) {
361 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(centroids[label_left], SubclavianArtery, i, p);
364 // Set Background and ForegroundValue according to label_right
365 MaskSlicePointer object =
366 clitk::Binarize<MaskSliceType>(slices_subclavianartery[i], label_left, label_left,
367 GetBackgroundValue(), GetForegroundValue());
369 // Relative Position : not at Right
370 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskSliceType> RelPosFilterType;
371 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
372 relPosFilter->VerboseStepFlagOff();
373 relPosFilter->WriteStepFlagOff();
374 relPosFilter->SetBackgroundValue(GetBackgroundValue());
375 relPosFilter->SetInput(slices_support[i]);
376 relPosFilter->SetInputObject(object);
377 relPosFilter->AddOrientationTypeString("L");
378 relPosFilter->SetInverseOrientationFlag(true);
379 // relPosFilter->SetIntermediateSpacing(3);
380 relPosFilter->SetIntermediateSpacingFlag(false);
381 relPosFilter->SetFuzzyThreshold(0.7);
382 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
383 relPosFilter->Update();
384 slices_support[i] = relPosFilter->GetOutput();
386 // Relative Position : not Anterior
387 relPosFilter = RelPosFilterType::New();
388 relPosFilter->VerboseStepFlagOff();
389 relPosFilter->WriteStepFlagOff();
390 relPosFilter->SetBackgroundValue(GetBackgroundValue());
391 relPosFilter->SetInput(slices_support[i]);
392 relPosFilter->SetInputObject(object);
393 relPosFilter->AddOrientationTypeString("A");
394 relPosFilter->SetInverseOrientationFlag(true);
395 // relPosFilter->SetIntermediateSpacing(3);
396 relPosFilter->SetIntermediateSpacingFlag(false);
397 relPosFilter->SetFuzzyThreshold(0.7);
398 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
399 relPosFilter->Update();
400 slices_support[i] = relPosFilter->GetOutput();
405 } // if centroids.size > 1
409 m_Working_Support = clitk::JoinSlices<MaskImageType>(slices_support, m_Working_Support, 2);
412 clitk::WriteListOfLandmarks<MaskImageType>(points, "subcl-lr.txt");
415 StopCurrentStep<MaskImageType>(m_Working_Support);
416 m_ListOfStations["3P"] = m_Working_Support;
418 //--------------------------------------------------------------------
420 //--------------------------------------------------------------------
421 template <class ImageType>
423 clitk::ExtractLymphStationsFilter<ImageType>::
424 ExtractStation_3P_LR_sup_Limits_2()
427 Use VertebralArtery to limit.
430 StartNewStep("[Station 3P] Left/Right limits with VertebralArtery");
433 MaskImagePointer VertebralArtery = GetAFDB()->template GetImage<MaskImageType>("VertebralArtery");
435 clitk::AndNot<MaskImageType>(m_Working_Support, VertebralArtery);
437 StopCurrentStep<MaskImageType>(m_Working_Support);
438 m_ListOfStations["3P"] = m_Working_Support;
441 //--------------------------------------------------------------------
443 //--------------------------------------------------------------------
444 template <class ImageType>
446 clitk::ExtractLymphStationsFilter<ImageType>::
447 ExtractStation_3P_LR_inf_Limits()
450 "On the right side, the limit is defined by the air–soft-tissue
451 interface. On the left side, it is defined by the air–tissue
452 interface superiorly (Fig. 2A–C) and the aorta inferiorly
453 (Figs. 2D–I and 3A–C)."
455 inf : not Right to Azygousvein
457 StartNewStep("[Station 3P] Left/Right limits (inferior part) with Azygousvein and Aorta");
460 MaskImagePointer AzygousVein = GetAFDB()->template GetImage<MaskImageType>("AzygousVein");
461 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
463 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskImageType> RelPosFilterType;
464 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
465 relPosFilter->VerboseStepFlagOff();
466 relPosFilter->WriteStepFlagOff();
467 relPosFilter->SetBackgroundValue(GetBackgroundValue());
468 relPosFilter->SetInput(m_Working_Support);
469 relPosFilter->SetInputObject(AzygousVein);
470 relPosFilter->AddOrientationTypeString("R");
471 relPosFilter->SetInverseOrientationFlag(true);
472 // relPosFilter->SetIntermediateSpacing(3);
473 relPosFilter->SetIntermediateSpacingFlag(false);
474 relPosFilter->SetFuzzyThreshold(0.7);
475 relPosFilter->AutoCropFlagOn();
476 relPosFilter->Update();
477 m_Working_Support = relPosFilter->GetOutput();
479 writeImage<MaskImageType>(m_Working_Support, "before-L-aorta.mhd");
480 relPosFilter->SetInput(m_Working_Support);
481 relPosFilter->SetInputObject(Aorta);
482 relPosFilter->AddOrientationTypeString("L");
483 relPosFilter->SetInverseOrientationFlag(true);
484 // relPosFilter->SetIntermediateSpacing(3);
485 relPosFilter->SetIntermediateSpacingFlag(false);
486 relPosFilter->SetFuzzyThreshold(0.7);
487 relPosFilter->AutoCropFlagOn();
488 relPosFilter->Update();
489 m_Working_Support = relPosFilter->GetOutput();
490 writeImage<MaskImageType>(m_Working_Support, "after-L-aorta.mhd");
492 StopCurrentStep<MaskImageType>(m_Working_Support);
493 m_ListOfStations["3P"] = m_Working_Support;
495 //--------------------------------------------------------------------