2 #include <itkBinaryDilateImageFilter.h>
3 #include <itkMirrorPadImageFilter.h>
5 //--------------------------------------------------------------------
6 template <class ImageType>
8 clitk::ExtractLymphStationsFilter<ImageType>::
9 ExtractStation_3P_SetDefaultValues()
12 //--------------------------------------------------------------------
15 //--------------------------------------------------------------------
16 template <class ImageType>
18 clitk::ExtractLymphStationsFilter<ImageType>::
19 ExtractStation_3P_SI_Limits()
22 Apex of the chest & Carina.
24 StartNewStep("[Station 3P] Inf/Sup limits with apex of the chest and carina");
26 // Get Carina position (has been determined in Station8)
27 m_CarinaZ = GetAFDB()->GetDouble("CarinaZ");
29 // Get Apex of the Chest. The "lungs" structure is autocroped so we
30 // consider the most superior point.
31 MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
32 MaskImageIndexType index = Lungs->GetLargestPossibleRegion().GetIndex();
33 index += Lungs->GetLargestPossibleRegion().GetSize();
35 Lungs->TransformIndexToPhysicalPoint(index, p);
36 p[2] -= 5; // 5 mm below because the autocrop is slightly above the apex
37 double m_ApexOfTheChest = p[2];
40 Superior limit = carina
41 Inferior limit = Apex of the chest */
43 clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
45 m_ApexOfTheChest, true,
46 GetBackgroundValue());
48 StopCurrentStep<MaskImageType>(m_Working_Support);
49 m_ListOfStations["3P"] = m_Working_Support;
51 //--------------------------------------------------------------------
54 //--------------------------------------------------------------------
55 template <class ImageType>
57 clitk::ExtractLymphStationsFilter<ImageType>::
58 ExtractStation_3P_Remove_Structures()
61 3 - (sup) remove Aorta, VB, subcl
62 not LR subcl ! -> a séparer LR ?
63 (inf) remove Eso, Aorta, Azygousvein
66 StartNewStep("[Station 3P] Remove some structures.");
68 Remove_Structures("Aorta");
69 Remove_Structures("VertebralBody");
70 Remove_Structures("SubclavianArtery");
71 Remove_Structures("Esophagus");
72 Remove_Structures("Azygousvein");
73 Remove_Structures("Thyroid");
74 Remove_Structures("VertebralArtery");
76 StopCurrentStep<MaskImageType>(m_Working_Support);
77 m_ListOfStations["3P"] = m_Working_Support;
79 //--------------------------------------------------------------------
82 //--------------------------------------------------------------------
83 template <class ImageType>
85 clitk::ExtractLymphStationsFilter<ImageType>::
86 ExtractStation_3P_Ant_Limits()
91 Anteriorly, it is in contact with the posterior aspect of Stations
92 1–2 superiorly (Fig. 2A–C) and with Stations 4R and 4L inferiorly
93 (Fig. 2D–I and 3A–C). The anterior limit of Station 3P is kept
94 posterior to the trachea, which is defined by an imaginary
95 horizontal line running along the posterior wall of the trachea
96 (Fig. 2B,E, red line). Posteriorly, it is delineated along the
97 anterior and lateral borders of the vertebral body until an
98 imaginary horizontal line running 1 cm posteriorly to the
99 anterior border of the vertebral body (Fig. 2D).
101 1 - post to the trachea : define an imaginary line just post the
102 Trachea and remove what is anterior.
105 StartNewStep("[Station 3P] Ant limits with Trachea ");
108 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
110 // Crop like current support (need by SliceBySliceSetBackgroundFromLineSeparation after)
112 clitk::ResizeImageLike<MaskImageType>(Trachea, m_Working_Support, GetBackgroundValue());
114 // Slice by slice, determine the most post point of the trachea (A)
115 // and consider a second point on the same horizontal line (B)
116 std::vector<MaskImagePointType> p_A;
117 std::vector<MaskImagePointType> p_B;
118 std::vector<typename MaskSliceType::Pointer> slices;
119 clitk::ExtractSlices<MaskImageType>(Trachea, 2, slices);
120 MaskImagePointType p;
121 typename MaskSliceType::PointType sp;
122 for(uint i=0; i<slices.size(); i++) {
123 // First only consider the main CCL (trachea, not bronchus)
124 slices[i] = Labelize<MaskSliceType>(slices[i], 0, true, 100);
125 slices[i] = KeepLabels<MaskSliceType>(slices[i], GetBackgroundValue(),
126 GetForegroundValue(), 1, 1, true);
127 // Find most posterior point
128 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i], GetBackgroundValue(),
130 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp, Trachea, i, p);
135 clitk::WriteListOfLandmarks<MaskImageType>(p_A, "trachea-post.txt");
137 // Remove Ant part above those lines
138 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
140 GetBackgroundValue(),
142 // End, release memory
143 GetAFDB()->template ReleaseImage<MaskImageType>("Trachea");
144 StopCurrentStep<MaskImageType>(m_Working_Support);
145 m_ListOfStations["3P"] = m_Working_Support;
147 //--------------------------------------------------------------------
150 //--------------------------------------------------------------------
151 template <class ImageType>
153 clitk::ExtractLymphStationsFilter<ImageType>::
154 ExtractStation_3P_Post_Limits()
159 Anteriorly, it is in contact with the posterior aspect of Stations
160 1–2 superiorly (Fig. 2A–C) and with Stations 4R and 4L inferiorly
161 (Fig. 2D–I and 3A–C). The anterior limit of Station 3P is kept
162 posterior to the trachea, which is defined by an imaginary
163 horizontal line running along the posterior wall of the trachea
164 (Fig. 2B,E, red line). Posteriorly, it is delineated along the
165 anterior and lateral borders of the vertebral body until an
166 imaginary horizontal line running 1 cm posteriorly to the
167 anterior border of the vertebral body (Fig. 2D).
169 2 - post to the trachea : define an imaginary line just post the
170 Trachea and remove what is anterior.
173 StartNewStep("[Station 3P] Post limits with VertebralBody ");
175 // Load VertebralBody
176 MaskImagePointer VertebralBody = GetAFDB()->template GetImage<MaskImageType>("VertebralBody");
178 // Crop like current support (need by SliceBySliceSetBackgroundFromLineSeparation after)
179 VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
181 writeImage<MaskImageType>(VertebralBody, "vb.mhd");
183 // Compute VertebralBody most Ant position (again because slices
184 // changes). Slice by slice, determine the most post point of the
185 // trachea (A) and consider a second point on the same horizontal
187 std::vector<MaskImagePointType> p_A;
188 std::vector<MaskImagePointType> p_B;
189 std::vector<typename MaskSliceType::Pointer> slices;
190 clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, slices);
191 MaskImagePointType p;
192 typename MaskSliceType::PointType sp;
193 for(uint i=0; i<slices.size(); i++) {
194 // Find most anterior point
195 bool found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i], GetBackgroundValue(),
198 // If the VertebralBody stop superiorly before the end of
199 // m_Working_Support, we consider the same previous point.
202 p[2] += VertebralBody->GetSpacing()[2];
205 p[2] += VertebralBody->GetSpacing()[2];
209 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp, VertebralBody, i, p);
210 p[1] += 10; // Consider 10 mm more post
216 clitk::WriteListOfLandmarks<MaskImageType>(p_A, "vb-ant.txt");
219 // Remove Ant part above those lines
220 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
222 GetBackgroundValue(),
224 writeImage<MaskImageType>(m_Working_Support, "a.mhd");
226 StopCurrentStep<MaskImageType>(m_Working_Support);
227 m_ListOfStations["3P"] = m_Working_Support;
229 //--------------------------------------------------------------------
232 //--------------------------------------------------------------------
233 template <class ImageType>
235 clitk::ExtractLymphStationsFilter<ImageType>::
236 ExtractStation_3P_LR_sup_Limits()
239 "On the right side, the limit is defined by the air–soft-tissue
240 interface. On the left side, it is defined by the air–tissue
241 interface superiorly (Fig. 2A–C) and the aorta inferiorly
242 (Figs. 2D–I and 3A–C)."
245 Resizelike support : Trachea, SubclavianArtery
246 Trachea, slice by slice, get centroid trachea
247 SubclavianArtery, slice by slice, CCL
248 prendre la 1ère à L et R, not at Left
251 StartNewStep("[Station 3P] Left/Right limits (superior part) ");
254 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
255 MaskImagePointer SubclavianArtery = GetAFDB()->template GetImage<MaskImageType>("SubclavianArtery");
257 // Crop like current support
258 Trachea = clitk::ResizeImageLike<MaskImageType>(Trachea, m_Working_Support, GetBackgroundValue());
259 SubclavianArtery = clitk::ResizeImageLike<MaskImageType>(SubclavianArtery, m_Working_Support, GetBackgroundValue());
261 writeImage<MaskImageType>(Trachea, "tr.mhd");
262 writeImage<MaskImageType>(SubclavianArtery, "sca.mhd");
264 // Get list of slices
265 std::vector<MaskSlicePointer> slices_support;
266 std::vector<MaskSlicePointer> slices_trachea;
267 std::vector<MaskSlicePointer> slices_subclavianartery;
268 clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices_support);
269 clitk::ExtractSlices<MaskImageType>(Trachea, 2, slices_trachea);
270 clitk::ExtractSlices<MaskImageType>(SubclavianArtery, 2, slices_subclavianartery);
273 std::vector<MaskImagePointType> points;
274 MaskImagePointType p;
275 for(uint i=0; i<slices_support.size(); i++) {
276 // Get Trachea centroid
277 std::vector<typename MaskSliceType::PointType> centroids;
278 typename MaskSliceType::PointType c;
279 ComputeCentroids<MaskSliceType>(slices_trachea[i], GetBackgroundValue(), centroids);
282 // [debug] Store point
283 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(centroids[1], Trachea, i, p);
286 // Get Right and Left CCL in SubclavianArtery
287 slices_subclavianartery[i] = Labelize<MaskSliceType>(slices_subclavianartery[i], 0, true, 10);
288 ComputeCentroids<MaskSliceType>(slices_subclavianartery[i], GetBackgroundValue(), centroids);
290 if (centroids.size() > 1) {
291 // Determine the one at Right/Left -> first after Trachea
293 typename MaskSliceType::PointType right;
294 typename MaskSliceType::PointType left;
299 for(uint j=1; j<centroids.size(); j++) {
300 if (centroids[j][0] < c[0]) { // At Right of Trachea centroid
301 if (centroids[j][0] >= right[0]) {
302 right = centroids[j];
306 if (centroids[j][0] > c[0]) { // At Left of Trachea centroid
307 if (centroids[j][0] <= left[0]) {
314 if (label_right != -1) {
317 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(centroids[label_right], SubclavianArtery, i, p);
320 // Set Background and ForegroundValue according to label_right
321 MaskSlicePointer object =
322 clitk::Binarize<MaskSliceType>(slices_subclavianartery[i], label_right, label_right,
323 GetBackgroundValue(), GetForegroundValue());
325 // Relative Position : not at Right
326 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskSliceType> RelPosFilterType;
327 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
328 relPosFilter->VerboseStepFlagOff();
329 relPosFilter->WriteStepFlagOff();
330 relPosFilter->SetBackgroundValue(GetBackgroundValue());
331 relPosFilter->SetInput(slices_support[i]);
332 relPosFilter->SetInputObject(object);
333 relPosFilter->AddOrientationTypeString("R");
334 relPosFilter->SetInverseOrientationFlag(true);
335 // relPosFilter->SetIntermediateSpacing(3);
336 relPosFilter->SetIntermediateSpacingFlag(false);
337 relPosFilter->SetFuzzyThreshold(0.7);
338 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
339 relPosFilter->Update();
340 slices_support[i] = relPosFilter->GetOutput();
342 // Relative Position : not Anterior
343 relPosFilter = RelPosFilterType::New();
344 relPosFilter->VerboseStepFlagOff();
345 relPosFilter->WriteStepFlagOff();
346 relPosFilter->SetBackgroundValue(GetBackgroundValue());
347 relPosFilter->SetInput(slices_support[i]);
348 relPosFilter->SetInputObject(object);
349 relPosFilter->AddOrientationTypeString("A");
350 relPosFilter->SetInverseOrientationFlag(true);
351 // relPosFilter->SetIntermediateSpacing(3);
352 relPosFilter->SetIntermediateSpacingFlag(false);
353 relPosFilter->SetFuzzyThreshold(0.7);
354 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
355 relPosFilter->Update();
356 slices_support[i] = relPosFilter->GetOutput();
358 } // End RelativePosition for Right
361 if (label_left != -1) {
364 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(centroids[label_left], SubclavianArtery, i, p);
367 // Set Background and ForegroundValue according to label_right
368 MaskSlicePointer object =
369 clitk::Binarize<MaskSliceType>(slices_subclavianartery[i], label_left, label_left,
370 GetBackgroundValue(), GetForegroundValue());
372 // Relative Position : not at Right
373 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskSliceType> RelPosFilterType;
374 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
375 relPosFilter->VerboseStepFlagOff();
376 relPosFilter->WriteStepFlagOff();
377 relPosFilter->SetBackgroundValue(GetBackgroundValue());
378 relPosFilter->SetInput(slices_support[i]);
379 relPosFilter->SetInputObject(object);
380 relPosFilter->AddOrientationTypeString("L");
381 relPosFilter->SetInverseOrientationFlag(true);
382 // relPosFilter->SetIntermediateSpacing(3);
383 relPosFilter->SetIntermediateSpacingFlag(false);
384 relPosFilter->SetFuzzyThreshold(0.7);
385 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
386 relPosFilter->Update();
387 slices_support[i] = relPosFilter->GetOutput();
389 // Relative Position : not Anterior
390 relPosFilter = RelPosFilterType::New();
391 relPosFilter->VerboseStepFlagOff();
392 relPosFilter->WriteStepFlagOff();
393 relPosFilter->SetBackgroundValue(GetBackgroundValue());
394 relPosFilter->SetInput(slices_support[i]);
395 relPosFilter->SetInputObject(object);
396 relPosFilter->AddOrientationTypeString("A");
397 relPosFilter->SetInverseOrientationFlag(true);
398 // relPosFilter->SetIntermediateSpacing(3);
399 relPosFilter->SetIntermediateSpacingFlag(false);
400 relPosFilter->SetFuzzyThreshold(0.7);
401 relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop
402 relPosFilter->Update();
403 slices_support[i] = relPosFilter->GetOutput();
408 } // if centroids.size > 1
412 m_Working_Support = clitk::JoinSlices<MaskImageType>(slices_support, m_Working_Support, 2);
415 clitk::WriteListOfLandmarks<MaskImageType>(points, "subcl-lr.txt");
418 StopCurrentStep<MaskImageType>(m_Working_Support);
419 m_ListOfStations["3P"] = m_Working_Support;
421 //--------------------------------------------------------------------
423 //--------------------------------------------------------------------
424 template <class ImageType>
426 clitk::ExtractLymphStationsFilter<ImageType>::
427 ExtractStation_3P_LR_sup_Limits_2()
430 Use VertebralArtery to limit.
433 StartNewStep("[Station 3P] Left/Right limits with VertebralArtery");
436 MaskImagePointer VertebralArtery = GetAFDB()->template GetImage<MaskImageType>("VertebralArtery");
438 clitk::AndNot<MaskImageType>(m_Working_Support, VertebralArtery);
440 StopCurrentStep<MaskImageType>(m_Working_Support);
441 m_ListOfStations["3P"] = m_Working_Support;
444 //--------------------------------------------------------------------
446 //--------------------------------------------------------------------
447 template <class ImageType>
449 clitk::ExtractLymphStationsFilter<ImageType>::
450 ExtractStation_3P_LR_inf_Limits()
453 "On the right side, the limit is defined by the air–soft-tissue
454 interface. On the left side, it is defined by the air–tissue
455 interface superiorly (Fig. 2A–C) and the aorta inferiorly
456 (Figs. 2D–I and 3A–C)."
458 inf : not Right to Azygousvein
460 StartNewStep("[Station 3P] Left/Right limits (inferior part) with Azygousvein and Aorta");
463 MaskImagePointer AzygousVein = GetAFDB()->template GetImage<MaskImageType>("AzygousVein");
464 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
466 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskImageType> RelPosFilterType;
467 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
468 relPosFilter->VerboseStepFlagOff();
469 relPosFilter->WriteStepFlagOff();
470 relPosFilter->SetBackgroundValue(GetBackgroundValue());
471 relPosFilter->SetInput(m_Working_Support);
472 relPosFilter->SetInputObject(AzygousVein);
473 relPosFilter->AddOrientationTypeString("R");
474 relPosFilter->SetInverseOrientationFlag(true);
475 // relPosFilter->SetIntermediateSpacing(3);
476 relPosFilter->SetIntermediateSpacingFlag(false);
477 relPosFilter->SetFuzzyThreshold(0.7);
478 relPosFilter->AutoCropFlagOn();
479 relPosFilter->Update();
480 m_Working_Support = relPosFilter->GetOutput();
482 writeImage<MaskImageType>(m_Working_Support, "before-L-aorta.mhd");
483 relPosFilter->SetInput(m_Working_Support);
484 relPosFilter->SetInputObject(Aorta);
485 relPosFilter->AddOrientationTypeString("L");
486 relPosFilter->SetInverseOrientationFlag(true);
487 // relPosFilter->SetIntermediateSpacing(3);
488 relPosFilter->SetIntermediateSpacingFlag(false);
489 relPosFilter->SetFuzzyThreshold(0.7);
490 relPosFilter->AutoCropFlagOn();
491 relPosFilter->Update();
492 m_Working_Support = relPosFilter->GetOutput();
493 writeImage<MaskImageType>(m_Working_Support, "after-L-aorta.mhd");
495 StopCurrentStep<MaskImageType>(m_Working_Support);
496 m_ListOfStations["3P"] = m_Working_Support;
498 //--------------------------------------------------------------------