2 #include <itkBinaryDilateImageFilter.h>
3 #include <itkMirrorPadImageFilter.h>
5 //--------------------------------------------------------------------
6 template <class ImageType>
8 clitk::ExtractLymphStationsFilter<ImageType>::
9 ExtractStation_8_SetDefaultValues()
11 SetDistanceMaxToAnteriorPartOfTheSpine(10);
13 p[0] = 15; p[1] = 2; p[2] = 1;
14 SetEsophagusDiltationForAnt(p);
15 p[0] = 5; p[1] = 10; p[2] = 1;
16 SetEsophagusDiltationForRight(p);
17 SetFuzzyThreshold("8", "Esophagus", 0.5);
18 SetInjectedThresholdForS8(150);
20 //--------------------------------------------------------------------
22 //--------------------------------------------------------------------
23 template <class ImageType>
25 clitk::ExtractLymphStationsFilter<ImageType>::
26 ExtractStation_8_SI_Limits()
29 Station 8: paraeosphageal nodes
31 "Superiorly, Station 8 begins at the level of the carina and,
32 therefore, abuts Station 3P (Fig. 3C). Inferiorly, the lower limit
33 of Station 8 was not specified in the Mountain and Dresler
34 classification (1). We delineate this volume until it reaches the
35 gastroesphogeal junction. "
37 => new classification IASCL 2009:
39 "Lower border: the diaphragm"
41 "Upper border: the upper border of the lower lobe bronchus on the
42 left; the lower border of the bronchus intermedius on the right"
45 StartNewStep("[Station8] Inf/Sup mediastinum limits with carina/diaphragm junction");
47 // Get Carina Z position
48 MaskImagePointer Carina = GetAFDB()->template GetImage<MaskImageType>("Carina");
50 std::vector<MaskImagePointType> centroids;
51 clitk::ComputeCentroids<MaskImageType>(Carina, GetBackgroundValue(), centroids);
52 m_CarinaZ = centroids[1][2];
54 // add one slice to include carina ?
55 m_CarinaZ += m_Mediastinum->GetSpacing()[2];
56 // We dont need Carina structure from now
58 GetAFDB()->SetDouble("CarinaZ", m_CarinaZ);
60 // Found most inferior part of the lung
61 MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
62 // It should be already croped, so I took the origin and add 10mm above
63 m_DiaphragmInferiorLimit = Lungs->GetOrigin()[2]+10;
64 // Lungs->Delete(); // we don't need it, release memory -> it we want to release, also free in AFDB
65 clitk::PrintMemory(GetVerboseMemoryFlag(), "after reading lungs");
66 GetAFDB()->template ReleaseImage<MaskImageType>("Lungs");
67 clitk::PrintMemory(GetVerboseMemoryFlag(), "after release lungs");
70 Superior limit = carina
71 Inferior limit = DiaphragmInferiorLimit (old=Gastroesphogeal junction) */
73 clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
74 m_DiaphragmInferiorLimit, //GOjunctionZ,
76 GetBackgroundValue());
78 // Remove some structures that we know are excluded
79 MaskImagePointer VertebralBody =
80 GetAFDB()->template GetImage <MaskImageType>("VertebralBody");
81 MaskImagePointer Aorta =
82 GetAFDB()->template GetImage <MaskImageType>("Aorta");
84 typedef clitk::BooleanOperatorLabelImageFilter<MaskImageType> BoolFilterType;
85 typename BoolFilterType::Pointer boolFilter = BoolFilterType::New();
86 boolFilter->InPlaceOn();
87 boolFilter->SetInput1(m_Working_Support);
88 boolFilter->SetInput2(VertebralBody);
89 boolFilter->SetOperationType(BoolFilterType::AndNot);
93 DO NOT REMOVE AORTA YET (LATER)
95 boolFilter->SetInput1(boolFilter->GetOutput());
96 boolFilter->SetInput2(Aorta);
97 boolFilter->SetOperationType(BoolFilterType::AndNot);
100 m_Working_Support = boolFilter->GetOutput();
103 StopCurrentStep<MaskImageType>(m_Working_Support);
104 m_ListOfStations["8"] = m_Working_Support;
106 //--------------------------------------------------------------------
109 //--------------------------------------------------------------------
110 template <class ImageType>
112 clitk::ExtractLymphStationsFilter<ImageType>::
113 ExtractStation_8_Post_Limits()
116 Station 8: paraeosphageal nodes
118 Anteriorly, it is in contact with Station 7 and the
119 left main stem bronchus in its superior aspect (Fig. 3C–H) and
120 with the heart more inferiorly. Posteriorly, Station 8 abuts the
121 descending aorta and the anterior aspect of the vertebral body
122 until an imaginary horizontal line running 1 cm posterior to the
123 anterior border of the vertebral body (Fig. 3C).
125 New classification IASCL 2009 :
127 "Nodes lying adjacent to the wall of the esophagus and to the
128 right or left of the midline, excluding subcarinal nodes (S7)
133 StartNewStep("[Station8] Post limits with vertebral body");
134 MaskImagePointer VertebralBody =
135 GetAFDB()->template GetImage <MaskImageType>("VertebralBody");
137 // Consider vertebral body slice by slice
138 typedef clitk::ExtractSliceFilter<MaskImageType> ExtractSliceFilterType;
139 typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
140 typedef typename ExtractSliceFilterType::SliceType SliceType;
141 std::vector<typename SliceType::Pointer> vertebralSlices;
142 extractSliceFilter->SetInput(VertebralBody);
143 extractSliceFilter->SetDirection(2);
144 extractSliceFilter->Update();
145 extractSliceFilter->GetOutputSlices(vertebralSlices);
147 // For each slice, compute the most anterior point
148 std::map<int, typename SliceType::PointType> vertebralAntPositionBySlice;
149 for(uint i=0; i<vertebralSlices.size(); i++) {
150 typename SliceType::PointType p;
151 bool found = clitk::FindExtremaPointInAGivenDirection<SliceType>(vertebralSlices[i],
152 GetBackgroundValue(),
155 vertebralAntPositionBySlice[i] = p;
158 // no Foreground in this slice (it appends generally at the
159 // beginning and the end of the volume). Do nothing in this
164 // Convert 2D points in slice into 3D points
165 std::vector<MaskImagePointType> vertebralAntPositions;
166 clitk::PointsUtils<MaskImageType>::Convert2DMapTo3DList(vertebralAntPositionBySlice,
168 vertebralAntPositions);
170 // DEBUG : write list of points
171 clitk::WriteListOfLandmarks<MaskImageType>(vertebralAntPositions,
172 "S8-vertebralMostAntPositions-points.txt");
174 // Cut support posteriorly 1cm the most anterior point of the
175 // VertebralBody. Do nothing below and above the VertebralBody. To
176 // do that compute several region, slice by slice and fill.
177 MaskImageRegionType region;
178 MaskImageSizeType size;
179 MaskImageIndexType start;
180 size[2] = 1; // a single slice
181 start[0] = m_Working_Support->GetLargestPossibleRegion().GetIndex()[0];
182 size[0] = m_Working_Support->GetLargestPossibleRegion().GetSize()[0];
183 for(uint i=0; i<vertebralAntPositions.size(); i++) {
184 typedef typename itk::ImageRegionIterator<MaskImageType> IteratorType;
186 IteratorType(m_Working_Support, m_Working_Support->GetLargestPossibleRegion());
187 MaskImageIndexType index;
188 // Consider some cm posterior to most anterior positions (usually
190 vertebralAntPositions[i][1] += GetDistanceMaxToAnteriorPartOfTheSpine();
191 // Get index of this point
192 m_Working_Support->TransformPhysicalPointToIndex(vertebralAntPositions[i], index);
193 // Compute region (a single slice)
195 start[1] = m_Working_Support->GetLargestPossibleRegion().GetIndex()[1]+index[1];
196 size[1] = m_Working_Support->GetLargestPossibleRegion().GetSize()[1]-start[1];
197 region.SetSize(size);
198 region.SetIndex(start);
200 if (m_Working_Support->GetLargestPossibleRegion().IsInside(start)) {
201 itk::ImageRegionIterator<MaskImageType> it(m_Working_Support, region);
203 while (!it.IsAtEnd()) {
204 it.Set(GetBackgroundValue());
210 StopCurrentStep<MaskImageType>(m_Working_Support);
211 m_ListOfStations["8"] = m_Working_Support;
213 //--------------------------------------------------------------------
216 //--------------------------------------------------------------------
217 template <class ImageType>
219 clitk::ExtractLymphStationsFilter<ImageType>::
220 ExtractStation_8_Ant_Sup_Limits()
222 //--------------------------------------------------------------------
223 StartNewStep("[Station8] Ant limits with S7 above Carina");
225 Anteriorly, it is in contact with Station 7 and the
226 left main stem bronchus in its superior aspect (Fig. 3C–H) and
227 with the heart more inferiorly.
229 1) line post wall bronchi (S7), till originRMLB
230 - LUL bronchus : to detect in trachea. But not needed here ??
231 2) heart ! -> not delineated.
232 ==> below S7, crop CT not to far away (ant), then try with threshold
234 1) ==> how to share with S7 ? Prepare both support at the same time !
235 NEED vector of initial support for each station ? No -> map if it exist before, take it !!
239 // Ant limit from carina (start) to end of S7 = originRMLB
241 MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
243 MaskImagePointer m_Working_Trachea =
244 clitk::CropImageAbove<MaskImageType>(Trachea, 2, m_CarinaZ, true, // AutoCrop
245 GetBackgroundValue());
247 // Seprate into two main bronchi
248 MaskImagePointer RightBronchus;
249 MaskImagePointer LeftBronchus;
251 // Labelize and consider the two first (main) labels
252 m_Working_Trachea = Labelize<MaskImageType>(m_Working_Trachea, 0, true, 1);
254 // Carina position must at the first slice that separate the two
255 // main bronchus (not superiorly). We thus first check that the
256 // upper slice is composed of at least two labels
257 typedef itk::ImageSliceIteratorWithIndex<MaskImageType> SliceIteratorType;
258 SliceIteratorType iter(m_Working_Trachea, m_Working_Trachea->GetLargestPossibleRegion());
259 iter.SetFirstDirection(0);
260 iter.SetSecondDirection(1);
261 iter.GoToReverseBegin(); // Start from the end (because image is IS not SI)
263 while (!iter.IsAtReverseEndOfSlice()) {
264 while (!iter.IsAtReverseEndOfLine()) {
265 if (iter.Get() > maxLabel) maxLabel = iter.Get();
271 clitkExceptionMacro("First slice form Carina does not seems to seperate the two main bronchus. Abort");
274 // Compute 3D centroids of both parts to identify the left from the
276 std::vector<ImagePointType> c;
277 clitk::ComputeCentroids<MaskImageType>(m_Working_Trachea, GetBackgroundValue(), c);
278 ImagePointType C1 = c[1];
279 ImagePointType C2 = c[2];
281 ImagePixelType rightLabel;
282 ImagePixelType leftLabel;
283 if (C1[0] < C2[0]) { rightLabel = 1; leftLabel = 2; }
284 else { rightLabel = 2; leftLabel = 1; }
286 // Select LeftLabel (set one label to Backgroundvalue)
288 clitk::Binarize<MaskImageType>(m_Working_Trachea, rightLabel, rightLabel,
289 GetBackgroundValue(), GetForegroundValue());
291 SetBackground<MaskImageType, MaskImageType>(m_Working_Trachea, m_Working_Trachea,
292 leftLabel, GetBackgroundValue(), false);
294 LeftBronchus = clitk::Binarize<MaskImageType>(m_Working_Trachea, leftLabel, leftLabel,
295 GetBackgroundValue(), GetForegroundValue());
297 SetBackground<MaskImageType, MaskImageType>(m_Working_Trachea, m_Working_Trachea,
298 rightLabel, GetBackgroundValue(), false);
302 RightBronchus = clitk::AutoCrop<MaskImageType>(RightBronchus, GetBackgroundValue());
303 LeftBronchus = clitk::AutoCrop<MaskImageType>(LeftBronchus, GetBackgroundValue());
305 // Insert int AFDB if need after
306 GetAFDB()->template SetImage <MaskImageType>("RightBronchus", "seg/rightBronchus.mhd",
307 RightBronchus, true);
308 GetAFDB()->template SetImage <MaskImageType>("LeftBronchus", "seg/leftBronchus.mhd",
311 // Now crop below OriginOfRightMiddleLobeBronchusZ
312 // It is not done before to keep entire bronchi.
314 MaskImagePointer OriginOfRightMiddleLobeBronchus =
315 GetAFDB()->template GetImage<MaskImageType>("OriginOfRightMiddleLobeBronchus");
316 std::vector<MaskImagePointType> centroids;
317 clitk::ComputeCentroids<MaskImageType>(OriginOfRightMiddleLobeBronchus, GetBackgroundValue(), centroids);
318 m_OriginOfRightMiddleLobeBronchusZ = centroids[1][2];
319 // add one slice to include carina ?
320 m_OriginOfRightMiddleLobeBronchusZ += RightBronchus->GetSpacing()[2];
321 // We dont need Carina structure from now
322 OriginOfRightMiddleLobeBronchus->Delete();
325 clitk::CropImageBelow<MaskImageType>(RightBronchus, 2,
326 m_OriginOfRightMiddleLobeBronchusZ,
328 GetBackgroundValue());
330 clitk::CropImageBelow<MaskImageType>(LeftBronchus, 2,
331 m_OriginOfRightMiddleLobeBronchusZ,
333 GetBackgroundValue());
335 // Search for points that are the most left/post/ant and most
336 // right/post/ant of the left and right bronchus
337 // 15 = not 15 mm more distance than the middle point.
338 FindExtremaPointsInBronchus(RightBronchus, 0, 10, m_LeftMostInRightBronchus,
339 m_AntMostInRightBronchus, m_PostMostInRightBronchus);
341 FindExtremaPointsInBronchus(LeftBronchus, 1, 10, m_RightMostInLeftBronchus,
342 m_AntMostInLeftBronchus, m_PostMostInLeftBronchus);
344 // DEBUG : write the list of points
346 v.reserve(m_LeftMostInRightBronchus.size()+m_AntMostInRightBronchus.size()+
347 m_PostMostInRightBronchus.size());
348 v.insert(v.end(), m_LeftMostInRightBronchus.begin(), m_LeftMostInRightBronchus.end() );
349 v.insert(v.end(), m_AntMostInRightBronchus.begin(), m_AntMostInRightBronchus.end() );
350 v.insert(v.end(), m_PostMostInRightBronchus.begin(), m_PostMostInRightBronchus.end() );
351 clitk::WriteListOfLandmarks<MaskImageType>(v, "S8-RightBronchus-points.txt");
354 v.reserve(m_RightMostInLeftBronchus.size()+m_AntMostInLeftBronchus.size()+
355 m_PostMostInLeftBronchus.size());
356 v.insert(v.end(), m_RightMostInLeftBronchus.begin(), m_RightMostInLeftBronchus.end() );
357 v.insert(v.end(), m_AntMostInLeftBronchus.begin(), m_AntMostInLeftBronchus.end() );
358 v.insert(v.end(), m_PostMostInLeftBronchus.begin(), m_PostMostInLeftBronchus.end() );
359 clitk::WriteListOfLandmarks<MaskImageType>(v, "S8-LeftBronchus-points.txt");
362 v.reserve(m_PostMostInLeftBronchus.size()+m_PostMostInRightBronchus.size());
363 v.insert(v.end(), m_PostMostInLeftBronchus.begin(), m_PostMostInLeftBronchus.end() );
364 v.insert(v.end(), m_PostMostInRightBronchus.begin(), m_PostMostInRightBronchus.end() );
365 clitk::WriteListOfLandmarks<MaskImageType>(v, "S8-RightLeftBronchus-points.txt");
368 // Now uses these points to limit, slice by slice
369 // line is mainly horizontal, so mainDirection=1
370 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
371 m_PostMostInRightBronchus,
372 m_PostMostInLeftBronchus,
373 GetBackgroundValue(), 1, 10);
375 // Keep main 3D CCL :
376 m_Working_Support = Labelize<MaskImageType>(m_Working_Support, 0, false, 10);
377 m_Working_Support = KeepLabels<MaskImageType>(m_Working_Support,
378 GetBackgroundValue(),
379 GetForegroundValue(), 1, 1, true);
382 m_Working_Support = clitk::AutoCrop<MaskImageType>(m_Working_Support, GetBackgroundValue());
385 StopCurrentStep<MaskImageType>(m_Working_Support);
386 // m_ListOfStations["8"] = m_Working_Support;
390 //--------------------------------------------------------------------
391 template <class ImageType>
393 clitk::ExtractLymphStationsFilter<ImageType>::
394 ExtractStation_8_Ant_Inf_Limits()
397 //--------------------------------------------------------------------
398 StartNewStep("[Station8] Ant part: not post to Esophagus");
400 Consider Esophagus, dilate it and remove ant part. It remains part
401 on L & R, than can be partly removed by cutting what remains at
402 right of vertebral body.
406 m_Esophagus = GetAFDB()->template GetImage<MaskImageType>("Esophagus");
407 clitk::PrintMemory(GetVerboseMemoryFlag(), "after read Esophagus");
409 // In images from the original article, Atlas – UM, the oesophagus
410 //was included in nodal stations 3p and 8. Having said that, in the
411 //description for station 8, it indicates that “the delineation of
412 //station 8 is limited to the soft tissues surrounding the
413 //oesophagus”. In the recent article, The IASLC Lung Cancer Staging
414 //Project (J Thorac Oncol 4:5, 568-77), the images drawn by
415 //Dr. Aletta Frasier exclude this structure. From an oncological
416 //prospective, the oesophagus should be excluded from these nodal
419 /* NOT YET !! DO IT LATER
421 typedef clitk::BooleanOperatorLabelImageFilter<MaskImageType> BoolFilterType;
422 typename BoolFilterType::Pointer boolFilter = BoolFilterType::New();
423 boolFilter->InPlaceOn();
424 boolFilter->SetInput1(m_Working_Support);
425 boolFilter->SetInput2(m_Esophagus);
426 boolFilter->SetOperationType(BoolFilterType::AndNot);
427 boolFilter->Update();
428 m_Working_Support = boolFilter->GetOutput();
432 // Crop Esophagus : keep only below the OriginOfRightMiddleLobeBronchusZ
434 clitk::CropImageAbove<MaskImageType>(m_Esophagus, 2,
435 m_OriginOfRightMiddleLobeBronchusZ,
437 GetBackgroundValue());
439 // Dilate to keep only not Anterior positions
440 MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt();
442 // m_Esophagus = EnlargeEsophagusDilatationRadiusInferiorly(m_Esophagus);
444 m_Esophagus = clitk::Dilate<MaskImageType>(m_Esophagus,
446 GetBackgroundValue(),
447 GetForegroundValue(), true);
449 // Remove Anterior part according to this dilatated esophagus. Note:
450 // because we crop Esophagus with ORML, the support will also be
451 // croped in the same way. Here it is a desired feature. If we dont
452 // want, use SetIgnoreEmptySliceObject(true)
454 // In the new IASCL definition, it is not clear if sup limits is
455 // around carina or On the right, it is “the lower border of the
456 // bronchus intermedius”, indicated on the image set as a point
457 // (“lower border of the bronchus intermedius”)
459 typedef clitk::SliceBySliceRelativePositionFilter<MaskImageType> RelPosFilterType;
460 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
461 relPosFilter->VerboseStepFlagOff();
462 relPosFilter->WriteStepFlagOff();
463 relPosFilter->SetInput(m_Working_Support);
464 relPosFilter->SetInputObject(m_Esophagus);
465 relPosFilter->AddOrientationTypeString("PostTo");
466 // relPosFilter->InverseOrientationFlagOff();
467 relPosFilter->SetDirection(2); // Z axis
468 relPosFilter->UniqueConnectedComponentBySliceOff();
469 relPosFilter->SetIntermediateSpacing(3);
470 relPosFilter->IntermediateSpacingFlagOn();
471 relPosFilter->SetFuzzyThreshold(GetFuzzyThreshold("8", "Esophagus"));
472 relPosFilter->RemoveObjectFlagOff(); // Do not exclude here because it is dilated
473 relPosFilter->CombineWithOrFlagOff(); // NO !
474 relPosFilter->IgnoreEmptySliceObjectFlagOn();
475 relPosFilter->Update();
476 m_Working_Support = relPosFilter->GetOutput();
478 // AutoCrop (OR SbS ?)
479 m_Working_Support = clitk::AutoCrop<MaskImageType>(m_Working_Support, GetBackgroundValue());
481 StopCurrentStep<MaskImageType>(m_Working_Support);
482 m_ListOfStations["8"] = m_Working_Support;
484 //--------------------------------------------------------------------
487 //--------------------------------------------------------------------
488 template <class ImageType>
490 clitk::ExtractLymphStationsFilter<ImageType>::
491 ExtractStation_8_Ant_Injected_Limits()
494 //--------------------------------------------------------------------
495 StartNewStep("[Station8] Ant part (remove high density, injected part)");
497 // Consider initial image, crop to current support
498 ImagePointer working_input =
499 clitk::ResizeImageLike<ImageType>(m_Input,
501 (short)GetBackgroundValue());
504 typedef itk::BinaryThresholdImageFilter<ImageType, MaskImageType> BinarizeFilterType;
505 typename BinarizeFilterType::Pointer binarizeFilter = BinarizeFilterType::New();
506 binarizeFilter->SetInput(working_input);
507 binarizeFilter->SetLowerThreshold(GetInjectedThresholdForS8());
508 binarizeFilter->SetInsideValue(GetForegroundValue());
509 binarizeFilter->SetOutsideValue(GetBackgroundValue());
510 binarizeFilter->Update();
511 MaskImagePointer injected = binarizeFilter->GetOutput();
513 // Combine with current support
514 clitk::AndNot<MaskImageType>(m_Working_Support, injected, GetBackgroundValue());
516 StopCurrentStep<MaskImageType>(m_Working_Support);
517 m_ListOfStations["8"] = m_Working_Support;
519 //--------------------------------------------------------------------
523 //--------------------------------------------------------------------
524 template <class ImageType>
526 clitk::ExtractLymphStationsFilter<ImageType>::
527 ExtractStation_8_LR_1_Limits()
529 //--------------------------------------------------------------------
530 StartNewStep("[Station8] Left and Right (from Carina to PulmonaryTrunk): Right to LeftPulmonaryArtery");
533 We remove LeftPulmonaryArtery structure and what is at Left to
536 MaskImagePointer LeftPulmonaryArtery = GetAFDB()->template GetImage<MaskImageType>("LeftPulmonaryArtery");
538 // Relative Position : not at Left
539 typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskImageType> RelPosFilterType;
540 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
541 relPosFilter->VerboseStepFlagOff();
542 relPosFilter->WriteStepFlagOff();
543 relPosFilter->SetBackgroundValue(GetBackgroundValue());
544 relPosFilter->SetInput(m_Working_Support);
545 relPosFilter->SetInputObject(LeftPulmonaryArtery);
546 relPosFilter->RemoveObjectFlagOn(); // remove the object too
547 relPosFilter->AddOrientationTypeString("L");
548 relPosFilter->InverseOrientationFlagOn(); // Not at Left
549 relPosFilter->SetIntermediateSpacing(3);
550 relPosFilter->IntermediateSpacingFlagOn();
551 relPosFilter->SetFuzzyThreshold(0.7);
552 relPosFilter->AutoCropFlagOn();
553 relPosFilter->Update();
554 m_Working_Support = relPosFilter->GetOutput();
556 // Release LeftPulmonaryArtery
557 GetAFDB()->template ReleaseImage<MaskImageType>("LeftPulmonaryArtery");
559 StopCurrentStep<MaskImageType>(m_Working_Support);
560 m_ListOfStations["8"] = m_Working_Support;
562 //--------------------------------------------------------------------
565 //--------------------------------------------------------------------
566 template <class ImageType>
568 clitk::ExtractLymphStationsFilter<ImageType>::
569 ExtractStation_8_LR_2_Limits()
571 //--------------------------------------------------------------------
572 StartNewStep("[Station8] Left and Right (from PulmTrunk to OriginMiddleLobeBronchus) Right to line from Aorta to PulmonaryTrunk");
575 We consider a line from Left part of Aorta to left part of
576 PulmonaryTrunk and remove what is at Left.
579 // Load the structures
580 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
581 MaskImagePointer PulmonaryTrunk = GetAFDB()->template GetImage<MaskImageType>("PulmonaryTrunk");
583 // Resize like the PT and define the slices
584 MaskImagePointType min, max;
585 clitk::GetMinMaxPointPosition<MaskImageType>(PulmonaryTrunk, min, max);
586 Aorta = clitk::CropImageAlongOneAxis<MaskImageType>(Aorta, 2, min[2], max[2], false, GetBackgroundValue());
587 std::vector<MaskSlicePointer> slices_aorta;
588 clitk::ExtractSlices<MaskImageType>(Aorta, 2, slices_aorta);
589 std::vector<MaskSlicePointer> slices_PT;
590 clitk::ExtractSlices<MaskImageType>(PulmonaryTrunk, 2, slices_PT);
592 // Find the points at left
593 std::vector<MaskImagePointType> p_A;
594 std::vector<MaskImagePointType> p_B;
595 MaskImagePointType pA;
596 MaskImagePointType pB;
597 for(uint i=0; i<slices_PT.size(); i++) {
598 typename MaskSliceType::PointType ps;
599 // In Aorta (assume a single CCL)
601 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices_aorta[i], GetBackgroundValue(), 0, false, ps);
602 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(ps, Aorta, i, pA);
605 // In PT : generally 2 CCL, we keep the most at left
607 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices_PT[i], GetBackgroundValue(), 0, false, ps);
608 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(ps, PulmonaryTrunk, i, pB);
616 clitk::WriteListOfLandmarks<MaskImageType>(p_A, "S8-Aorta-Left-points.txt");
617 clitk::WriteListOfLandmarks<MaskImageType>(p_B, "S8-PT-Left-points.txt");
619 // Remove part at Left
620 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
622 GetBackgroundValue(),
625 StopCurrentStep<MaskImageType>(m_Working_Support);
626 m_ListOfStations["8"] = m_Working_Support;
628 //--------------------------------------------------------------------
631 //--------------------------------------------------------------------
632 template <class ImageType>
634 clitk::ExtractLymphStationsFilter<ImageType>::
635 ExtractStation_8_Single_CCL_Limits()
637 //--------------------------------------------------------------------
638 StartNewStep("[Station8 or 3P] Slice by slice, keep a single CCL (the closest to VertebralBody)");
641 std::vector<typename MaskSliceType::Pointer> slices;
642 std::vector<typename MaskSliceType::Pointer> slices_vb;
643 clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices);
644 MaskImagePointer VertebralBody =
645 GetAFDB()->template GetImage <MaskImageType>("VertebralBody");
646 VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
647 clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, slices_vb);
649 for(uint i=0; i<slices.size(); i++) {
650 // Decompose in labels
651 slices[i] = Labelize<MaskSliceType>(slices[i], 0, true, 100);
652 // Compute centroids coordinate
653 std::vector<typename MaskSliceType::PointType> centroids;
654 std::vector<typename MaskSliceType::PointType> c;
655 clitk::ComputeCentroids<MaskSliceType>(slices[i], GetBackgroundValue(), centroids);
656 clitk::ComputeCentroids<MaskSliceType>(slices_vb[i], GetBackgroundValue(), c);
657 if ((c.size() > 1) && (centroids.size() > 1)) {
658 // keep the one which is the closer to vertebralbody centroid
659 double distance=1000000;
661 for(uint j=1; j<centroids.size(); j++) {
662 double d = centroids[j].EuclideanDistanceTo(c[1]);
668 // remove all others label
669 slices[i] = KeepLabels<MaskSliceType>(slices[i], GetBackgroundValue(),
670 GetForegroundValue(), index, index, true);
673 m_Working_Support = clitk::JoinSlices<MaskImageType>(slices, m_Working_Support, 2);
675 StopCurrentStep<MaskImageType>(m_Working_Support);
676 m_ListOfStations["8"] = m_Working_Support;
678 //--------------------------------------------------------------------
681 //--------------------------------------------------------------------
682 template <class ImageType>
684 clitk::ExtractLymphStationsFilter<ImageType>::
685 ExtractStation_8_LR_Limits_old2()
688 //--------------------------------------------------------------------
689 StartNewStep("[Station8] Left and Right limits arround esophagus (below Carina)");
691 // Estract slices for current support for slice by slice processing
692 std::vector<typename MaskSliceType::Pointer> slices;
693 clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices);
695 // Dilate the Esophagus to consider a margins around
696 MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt();
697 m_Esophagus = clitk::Dilate<MaskImageType>(m_Esophagus,
699 GetBackgroundValue(),
700 GetForegroundValue(), true);
702 // Remove what is outside the mediastinum in this enlarged Esophagus -> it allows to select
703 // 'better' extrema points (not too post).
704 MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
705 clitk::AndNot<MaskImageType>(m_Esophagus, Lungs, GetBackgroundValue());
706 GetAFDB()->template ReleaseImage<MaskImageType>("Lungs");
708 // Estract slices of Esophagus (resize like support before to have the same set of slices)
709 MaskImagePointer EsophagusForSlice = clitk::ResizeImageLike<MaskImageType>(m_Esophagus, m_Working_Support, GetBackgroundValue());
711 std::vector<typename MaskSliceType::Pointer> eso_slices;
712 clitk::ExtractSlices<MaskImageType>(EsophagusForSlice, 2, eso_slices);
714 // Estract slices of Vertebral (resize like support before to have the same set of slices)
715 MaskImagePointer VertebralBody = GetAFDB()->template GetImage<MaskImageType>("VertebralBody");
716 VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
717 std::vector<typename MaskSliceType::Pointer> vert_slices;
718 clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, vert_slices);
720 // Estract slices of Aorta (resize like support before to have the same set of slices)
721 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
722 Aorta = clitk::ResizeImageLike<MaskImageType>(Aorta, m_Working_Support, GetBackgroundValue());
723 std::vector<typename MaskSliceType::Pointer> aorta_slices;
724 clitk::ExtractSlices<MaskImageType>(Aorta, 2, aorta_slices);
726 // Extract slices of Mediastinum (resize like support before to have the same set of slices)
727 m_Mediastinum = GetAFDB()->template GetImage<MaskImageType>("Mediastinum");
728 m_Mediastinum = clitk::ResizeImageLike<MaskImageType>(m_Mediastinum, m_Working_Support, GetBackgroundValue());
729 std::vector<typename MaskSliceType::Pointer> mediast_slices;
730 clitk::ExtractSlices<MaskImageType>(m_Mediastinum, 2, mediast_slices);
733 std::vector<MaskImagePointType> p_RightMostAnt;
734 std::vector<MaskImagePointType> p_RightMostPost;
735 std::vector<MaskImagePointType> p_LeftMostAnt;
736 std::vector<MaskImagePointType> p_LeftMostPost;
737 std::vector<MaskImagePointType> p_AllPoints;
738 std::vector<MaskImagePointType> p_LeftAorta;
739 std::vector<MaskImagePointType> p_LeftEso;
742 In the following, we search for the LeftRight limits. We search
743 for the most Right points in Esophagus and in VertebralBody and
744 consider a line between those to most right points. All points in
745 the support which are most right to this line are discarded. Same
746 for the left part. The underlying assumption is that the support
747 is concave between Eso/VertebralBody. Esophagus is a bit
748 dilatated. On VertebralBody we go right (or left) until we reach
749 the lung (but no more 20 mm).
753 MaskImagePointType p;
754 MaskImagePointType pp;
755 for(uint i=0; i<slices.size() ; i++) {
756 // Declare all needed points (sp = slice point)
757 typename MaskSliceType::PointType sp_maxRight_Eso;
758 typename MaskSliceType::PointType sp_maxRight_Aorta;
759 typename MaskSliceType::PointType sp_maxRight_Vertebra;
760 typename MaskSliceType::PointType sp_maxLeft_Eso;
761 typename MaskSliceType::PointType sp_maxLeft_Aorta;
762 typename MaskSliceType::PointType sp_maxLeft_Vertebra;
764 // Right is at left on screen, coordinate decrease
765 // Left is at right on screen, coordinate increase
767 // Find limit of Vertebral -> only at most Post part of current
768 // slice support. First found most ant point in VertebralBody
769 typedef MaskSliceType SliceType;
770 typename SliceType::PointType p_slice_ant;
771 bool found = clitk::FindExtremaPointInAGivenDirection<SliceType>(vert_slices[i], GetBackgroundValue(), 1, true, p_slice_ant);
773 // It should not happen ! But sometimes, a contour is missing or
774 // the VertebralBody is not delineated enough inferiorly ... in
775 // those cases, we consider the first found slice.
776 std::cerr << "No foreground pixels in this VertebralBody slices !?? I try with the previous/next slice" << std::endl;
780 found = clitk::FindExtremaPointInAGivenDirection<SliceType>(vert_slices[j], GetBackgroundValue(), 1, true, p_slice_ant);
781 //clitkExceptionMacro("No foreground pixels in this VertebralBody slices ??");
785 p_slice_ant[1] += GetDistanceMaxToAnteriorPartOfTheSpine(); // Consider offset
787 // The, find most Right and Left points on that AP position
788 typename SliceType::IndexType indexR;
789 typename SliceType::IndexType indexL;
790 vert_slices[i]->TransformPhysicalPointToIndex(p_slice_ant, indexR);
792 // Check that is inside the mask
793 indexR[1] = std::min(indexR[1], (long)vert_slices[i]->GetLargestPossibleRegion().GetSize()[1]-1);
794 indexL[1] = indexR[1];
795 while (vert_slices[i]->GetPixel(indexR) != GetBackgroundValue()) {
796 indexR[0] --; // Go to the right
798 while (vert_slices[i]->GetPixel(indexL) != GetBackgroundValue()) {
799 indexL[0] ++; // Go to the left
801 vert_slices[i]->TransformIndexToPhysicalPoint(indexR, sp_maxRight_Vertebra);
802 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Vertebra, VertebralBody, i, p);
803 p_AllPoints.push_back(p);
804 vert_slices[i]->TransformIndexToPhysicalPoint(indexL, sp_maxLeft_Vertebra);
805 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Vertebra, VertebralBody, i, p);
806 p_AllPoints.push_back(p);
808 // Find last point out of the mediastinum on this line, Right :
809 mediast_slices[i]->TransformPhysicalPointToIndex(sp_maxRight_Vertebra, indexR);
810 double distance = 0.0;
811 while (mediast_slices[i]->GetPixel(indexR) != GetBackgroundValue()) {
813 distance += mediast_slices[i]->GetSpacing()[0];
815 if (distance < 30) { // Ok in this case, we found limit with lung
816 mediast_slices[i]->TransformIndexToPhysicalPoint(indexR, sp_maxRight_Vertebra);
817 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Vertebra, m_Mediastinum, i, p);
819 else { // in that case, we are probably below the diaphragm, so we
820 // add aribtrarly few mm
821 sp_maxRight_Vertebra[0] -= 2; // Leave 2 mm around the VertebralBody
822 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Vertebra, m_Mediastinum, i, p);
824 p_RightMostPost.push_back(p);
825 p_AllPoints.push_back(p);
827 // Find last point out of the mediastinum on this line, Left :
828 mediast_slices[i]->TransformPhysicalPointToIndex(sp_maxLeft_Vertebra, indexL);
830 while (mediast_slices[i]->GetPixel(indexL) != GetBackgroundValue()) {
832 distance += mediast_slices[i]->GetSpacing()[0];
834 if (distance < 30) { // Ok in this case, we found limit with lung
835 mediast_slices[i]->TransformIndexToPhysicalPoint(indexL, sp_maxLeft_Vertebra);
836 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Vertebra, m_Mediastinum, i, p);
838 else { // in that case, we are probably below the diaphragm, so we
839 // add aribtrarly few mm
840 sp_maxLeft_Vertebra[0] += 2; // Leave 2 mm around the VertebralBody
841 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Vertebra, m_Mediastinum, i, p);
843 p_LeftMostPost.push_back(p);
844 p_AllPoints.push_back(p);
846 // Find Eso slice centroid and do not consider what is post to
848 std::vector<typename MaskSliceType::PointType> c;
849 clitk::ComputeCentroids<MaskSliceType>(eso_slices[i], GetBackgroundValue(), c);
852 clitk::CropImageAbove<MaskSliceType>(eso_slices[i], 1, c[1][1], false, GetBackgroundValue());
854 clitk::ResizeImageLike<MaskSliceType>(eso_slices[i], aorta_slices[i], GetBackgroundValue());
855 // writeImage<MaskSliceType>(eso_slices[i], "eso-slice-"+toString(i)+".mhd");
858 // Find right limit of Esophagus and Aorta
860 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(),
861 0, true, sp_maxRight_Eso);
863 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(aorta_slices[i], GetBackgroundValue(),
864 0, true, sp_maxRight_Aorta);
865 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Eso, EsophagusForSlice, i, p);
866 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Aorta, Aorta, i, pp);
867 pp[0] -= 2; // Add a margin of 2 mm to include the Aorta 'wall'
868 p_AllPoints.push_back(p);
870 p_AllPoints.push_back(pp);
871 MaskImagePointType A = p_RightMostPost.back();
872 MaskImagePointType B = p;
873 MaskImagePointType C = pp;
874 double s = (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]);
875 if (s>0) p_RightMostAnt.push_back(p);
876 else p_RightMostAnt.push_back(pp);
878 else { // No more Esophagus in this slice : do nothing
879 // p_RightMostAnt.push_back(p);
880 p_RightMostPost.pop_back();
883 // --------------------------------------------------------------------------
884 // Find the limit on the Left: most left point between Eso and
885 // Eso. (Left is left on screen, coordinate increase)
887 // Find left limit of Esophagus
888 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(), 0, false, sp_maxLeft_Eso);
889 f = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(aorta_slices[i], GetBackgroundValue(), 0, false, sp_maxLeft_Aorta);
890 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Eso, EsophagusForSlice, i, p);
891 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Aorta, Aorta, i, pp);
892 p_AllPoints.push_back(p);
893 pp[0] += 2; // Add a margin of 2 mm to include the 'wall'
894 if (f) { // not below Aorta
895 p_AllPoints.push_back(pp);
896 MaskImagePointType A = p_LeftMostPost.back();
897 MaskImagePointType B = p;
898 MaskImagePointType C = pp;
899 double s = (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]);
901 p_LeftMostAnt.push_back(p); // Insert point most at Left, Eso
904 // in this case -> two lines !
905 p_LeftMostAnt.push_back(pp); // Insert point most at Left, Aorta (Vert to Aorta)
906 // but also consider Aorta to Eso
907 p_LeftAorta.push_back(pp);
908 p_LeftEso.push_back(p);
911 else { // No more Esophagus in this slice : do nothing
912 p_LeftMostPost.pop_back();
913 //p_LeftMostAnt.push_back(p);
915 } // End of slice loop
917 clitk::WriteListOfLandmarks<MaskImageType>(p_AllPoints, "S8-LR-Eso-Vert.txt");
918 clitk::WriteListOfLandmarks<MaskImageType>(p_LeftEso, "S8-Left-Eso.txt");
919 clitk::WriteListOfLandmarks<MaskImageType>(p_LeftAorta, "S8-Left-Aorta.txt");
921 // Now uses these points to limit, slice by slice
922 // Line is mainly vertical, so mainDirection=0
923 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
924 p_RightMostAnt, p_RightMostPost,
925 GetBackgroundValue(), 0, 10);
926 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
927 p_LeftMostAnt, p_LeftMostPost,
928 GetBackgroundValue(), 0, -10);
929 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
930 p_LeftEso,p_LeftAorta,
931 GetBackgroundValue(), 0, -10);
933 StopCurrentStep<MaskImageType>(m_Working_Support);
934 m_ListOfStations["8"] = m_Working_Support;
937 //--------------------------------------------------------------------
940 //--------------------------------------------------------------------
941 template <class ImageType>
943 clitk::ExtractLymphStationsFilter<ImageType>::
944 ExtractStation_8_LR_Limits()
947 //--------------------------------------------------------------------
948 StartNewStep("[Station8] Left and Right limits arround esophagus with lines from VertebralBody-Aorta-Esophagus");
950 // Estract slices for current support for slice by slice processing
951 std::vector<typename MaskSliceType::Pointer> slices;
952 clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices);
954 // Dilate the Esophagus to consider a margins around
955 MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt();
956 m_Esophagus = clitk::Dilate<MaskImageType>(m_Esophagus, radiusInMM,
957 GetBackgroundValue(), GetForegroundValue(), true);
958 // m_Esophagus = EnlargeEsophagusDilatationRadiusInferiorly(m_Esophagus);
960 // Remove what is outside the mediastinum in this enlarged Esophagus -> it allows to select
961 // 'better' extrema points (not too post).
962 MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
963 clitk::AndNot<MaskImageType>(m_Esophagus, Lungs, GetBackgroundValue());
964 GetAFDB()->template ReleaseImage<MaskImageType>("Lungs");
966 // Estract slices of Esophagus (resize like support before to have the same set of slices)
967 MaskImagePointer EsophagusForSlice = clitk::ResizeImageLike<MaskImageType>(m_Esophagus, m_Working_Support, GetBackgroundValue());
968 std::vector<typename MaskSliceType::Pointer> eso_slices;
969 clitk::ExtractSlices<MaskImageType>(EsophagusForSlice, 2, eso_slices);
971 // Estract slices of Vertebral (resize like support before to have the same set of slices)
972 MaskImagePointer VertebralBody = GetAFDB()->template GetImage<MaskImageType>("VertebralBody");
973 VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
974 std::vector<typename MaskSliceType::Pointer> vert_slices;
975 clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, vert_slices);
977 // Estract slices of Aorta (resize like support before to have the same set of slices)
978 MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
979 Aorta = clitk::ResizeImageLike<MaskImageType>(Aorta, m_Working_Support, GetBackgroundValue());
980 std::vector<typename MaskSliceType::Pointer> aorta_slices;
981 clitk::ExtractSlices<MaskImageType>(Aorta, 2, aorta_slices);
983 // Extract slices of Mediastinum (resize like support before to have the same set of slices)
984 m_Mediastinum = GetAFDB()->template GetImage<MaskImageType>("Mediastinum");
985 m_Mediastinum = clitk::ResizeImageLike<MaskImageType>(m_Mediastinum, m_Working_Support, GetBackgroundValue());
986 std::vector<typename MaskSliceType::Pointer> mediast_slices;
987 clitk::ExtractSlices<MaskImageType>(m_Mediastinum, 2, mediast_slices);
990 std::vector<MaskImagePointType> p_MostLeftVertebralBody;
991 std::vector<MaskImagePointType> p_MostRightVertebralBody;
992 std::vector<MaskImagePointType> p_MostLeftAorta;
993 std::vector<MaskImagePointType> p_MostLeftEsophagus;
996 In the following, we search for the LeftRight limits. We search
997 for the most Right points in Esophagus and in VertebralBody and
998 consider a line between those to most right points. All points in
999 the support which are most right to this line are discarded. Same
1000 for the left part. The underlying assumption is that the support
1001 is concave between Eso/VertebralBody. Esophagus is a bit
1002 dilatated. On VertebralBody we go right (or left) until we reach
1003 the lung (but no more 20 mm).
1006 // Temporary 3D point
1007 MaskImagePointType p;
1010 for(uint i=0; i<slices.size() ; i++) {
1011 // Declare all needed 2D points (sp = slice point)
1012 typename MaskSliceType::PointType sp_MostRightVertebralBody;
1013 typename MaskSliceType::PointType sp_MostLeftVertebralBody;
1014 typename MaskSliceType::PointType sp_MostLeftAorta;
1015 typename MaskSliceType::PointType sp_temp;
1016 typename MaskSliceType::PointType sp_MostLeftEsophagus;
1018 // Right is at left on screen, coordinate decrease
1019 // Left is at right on screen, coordinate increase
1021 /* -------------------------------------------------------------------------------------
1022 Find most Left point in VertebralBody. Consider only the
1023 horizontal line which is 'DistanceMaxToAnteriorPartOfTheSpine'
1024 away from the most ant point.
1026 typename MaskSliceType::PointType sp_MostAntVertebralBody;
1030 found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[j], GetBackgroundValue(), 1, true, sp_MostAntVertebralBody);
1032 // It should not happen ! But sometimes, a contour is missing or
1033 // the VertebralBody is not delineated enough inferiorly ... in
1034 // those cases, we consider the first found slice.
1035 std::cerr << "No foreground pixels in this VertebralBody slices !?? I try with the previous/next slice" << std::endl;
1039 sp_MostAntVertebralBody[1] += GetDistanceMaxToAnteriorPartOfTheSpine(); // Consider offset
1041 // Crop the vertebralbody below this most post line
1043 clitk::CropImageAbove<MaskSliceType>(vert_slices[j], 1, sp_MostAntVertebralBody[1], false, GetBackgroundValue());
1045 clitk::ResizeImageLike<MaskSliceType>(vert_slices[j], aorta_slices[i], GetBackgroundValue());
1046 // writeImage<MaskSliceType>(vert_slices[i], "vert-slice-"+toString(i)+".mhd");
1048 // Find first point not in mediastinum
1049 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[j], GetBackgroundValue(), 0, false, sp_MostLeftVertebralBody);
1050 sp_MostLeftVertebralBody = clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, false, sp_MostLeftVertebralBody, GetBackgroundValue(), 30);
1051 sp_MostLeftVertebralBody[0] += 2; // 2mm margin
1052 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[j], GetBackgroundValue(), 0, true, sp_MostRightVertebralBody);
1053 sp_MostRightVertebralBody = clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, true, sp_MostRightVertebralBody, GetBackgroundValue(),30);
1054 sp_MostRightVertebralBody[0] -= 2; // 2 mm margin
1056 // Convert 2D points into 3D
1057 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostRightVertebralBody, VertebralBody, i, p);
1058 p_MostRightVertebralBody.push_back(p);
1059 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p);
1060 p_MostLeftVertebralBody.push_back(p);
1062 /* -------------------------------------------------------------------------------------
1063 Find most Left point in Esophagus
1065 found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(), 0, false, sp_MostLeftEsophagus);
1066 if (!found) { // No more Esophagus, I remove the previous point
1068 // if (p_MostLeftEsophagus.size() < 1) {
1069 p_MostLeftVertebralBody.pop_back();
1072 // // Consider the previous point
1073 // p = p_MostLeftEsophagus.back();
1074 // p_MostLeftEsophagus.push_back(p);
1075 // sp_MostLeftEsophagus = sp_temp; // Retrieve previous 2D position
1080 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftEsophagus, EsophagusForSlice, i, p);
1081 p_MostLeftEsophagus.push_back(p);
1082 // sp_temp = sp_MostLeftEsophagus; // Store previous 2D position
1085 /* -------------------------------------------------------------------------------------
1086 Find most Left point in Aorta
1089 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(aorta_slices[i], GetBackgroundValue(), 0, false, sp_MostLeftAorta);
1090 sp_MostLeftAorta = clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, false, sp_MostLeftAorta, GetBackgroundValue(), 10);
1091 typename MaskSliceType::PointType temp=sp_MostLeftEsophagus;
1093 if (clitk::IsOnTheSameLineSide(sp_MostLeftAorta, sp_MostLeftVertebralBody, sp_MostLeftEsophagus, temp)) {
1094 // sp_MostLeftAorta is on the same side than temp (at Right) -> ignore Aorta
1095 sp_MostLeftAorta = sp_MostLeftEsophagus;
1097 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftAorta, Aorta, i, p);
1098 p_MostLeftAorta.push_back(p);
1101 } // End of slice loop
1103 clitk::WriteListOfLandmarks<MaskImageType>(p_MostLeftVertebralBody, "S8-MostLeft-VB-points.txt");
1104 clitk::WriteListOfLandmarks<MaskImageType>(p_MostRightVertebralBody, "S8-MostRight-VB-points.txt");
1105 clitk::WriteListOfLandmarks<MaskImageType>(p_MostLeftAorta, "S8-MostLeft-Aorta-points.txt");
1106 clitk::WriteListOfLandmarks<MaskImageType>(p_MostLeftEsophagus, "S8-MostLeft-eso-points.txt");
1108 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
1109 p_MostLeftVertebralBody, p_MostLeftAorta,
1110 GetBackgroundValue(), 0, -10);
1112 clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
1113 p_MostLeftAorta, p_MostLeftEsophagus,
1114 GetBackgroundValue(), 0, -10);
1117 StopCurrentStep<MaskImageType>(m_Working_Support);
1118 m_ListOfStations["8"] = m_Working_Support;
1121 //--------------------------------------------------------------------
1124 //--------------------------------------------------------------------
1125 template <class ImageType>
1127 clitk::ExtractLymphStationsFilter<ImageType>::
1128 ExtractStation_8_Remove_Structures()
1131 //--------------------------------------------------------------------
1132 StartNewStep("[Station8] remove some structures");
1134 Remove_Structures("8", "Aorta");
1135 Remove_Structures("8", "Esophagus");
1138 StopCurrentStep<MaskImageType>(m_Working_Support);
1139 m_ListOfStations["8"] = m_Working_Support;
1142 //--------------------------------------------------------------------
1145 //--------------------------------------------------------------------
1146 template <class ImageType>
1147 typename clitk::ExtractLymphStationsFilter<ImageType>::MaskImagePointer
1148 clitk::ExtractLymphStationsFilter<ImageType>::
1149 EnlargeEsophagusDilatationRadiusInferiorly(MaskImagePointer & Esophagus)
1151 // Check if Esophagus is delineated at least until Diaphragm. Now,
1152 // because we use AutoCrop, Origin[2] gives this max inferior
1155 DD("BUGGY DONT USE");
1158 if (Esophagus->GetOrigin()[2] > m_DiaphragmInferiorLimit) {
1159 // crop first slice without mask
1160 MaskImagePointType pt;
1161 clitk::FindExtremaPointInAGivenDirection<MaskImageType>(Esophagus, GetBackgroundValue(), 2, true, pt);
1164 clitk::CropImageBelow<MaskImageType>(Esophagus, 2,
1167 GetBackgroundValue());
1168 writeImage<MaskImageType>(Esophagus, "crop-eso.mhd");
1170 std::cout << "Warning Esophagus is not delineated until Diaphragm. I mirror-pad it."
1172 double extraSize = Esophagus->GetOrigin()[2]-m_DiaphragmInferiorLimit;
1174 // Pad with few more slices
1175 typedef itk::MirrorPadImageFilter<MaskImageType, MaskImageType> PadFilterType;
1176 typename PadFilterType::Pointer padFilter = PadFilterType::New();
1177 padFilter->SetInput(Esophagus);
1178 MaskImageSizeType b;
1179 b[0] = 0; b[1] = 0; b[2] = (uint)ceil(extraSize/Esophagus->GetSpacing()[2])+1;
1180 padFilter->SetPadLowerBound(b);
1181 padFilter->Update();
1182 Esophagus = padFilter->GetOutput();
1186 //--------------------------------------------------------------------
1189 //--------------------------------------------------------------------
1190 template <class ImageType>
1192 clitk::ExtractLymphStationsFilter<ImageType>::
1193 ExtractStation_8_LR_Limits_old()
1196 Station 8: paraeosphageal nodes
1198 Laterally, it is within the pleural envelope and again abuts the
1199 descending aorta on the left. Reasonably, the delineation of
1200 Station 8 is limited to the soft tissue surrounding the esophagus
1204 StartNewStep("[Station8] Right limits (around esophagus)");
1206 MaskImagePointer Esophagus = GetAFDB()->template GetImage<MaskImageType>("Esophagus");
1208 // Autocrop to get first slice with starting Esophagus
1209 Esophagus = clitk::AutoCrop<MaskImageType>(Esophagus, GetBackgroundValue());
1212 // LR dilatation -> large to keep point inside
1213 // AP dilatation -> few mm
1214 // SI dilatation -> enough to cover Diaphragm (old=GOjunctionZ)
1215 MaskImagePointType radiusInMM = GetEsophagusDiltationForRight();
1216 Esophagus = EnlargeEsophagusDilatationRadiusInferiorly(Esophagus);
1217 Esophagus = clitk::Dilate<MaskImageType>(Esophagus,
1219 GetBackgroundValue(),
1220 GetForegroundValue(), true);
1221 writeImage<MaskImageType>(Esophagus, "dilateEso2.mhd");
1223 writeImage<MaskImageType>(m_Working_Support, "before-relpos2.mhd");
1225 // Remove Right (Left on the screen) part according to this
1226 // dilatated esophagus
1227 typedef clitk::SliceBySliceRelativePositionFilter<MaskImageType> RelPosFilterType;
1228 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
1229 relPosFilter->VerboseStepFlagOff();
1230 relPosFilter->WriteStepFlagOff();
1231 relPosFilter->SetInput(m_Working_Support);
1232 relPosFilter->SetInputObject(Esophagus);
1233 relPosFilter->AddOrientationTypeString("NotLeftTo");
1234 // relPosFilter->InverseOrientationFlagOn(); // Not Left to
1235 relPosFilter->SetDirection(2); // Z axis
1236 relPosFilter->UniqueConnectedComponentBySliceOff(); // important
1237 relPosFilter->SetIntermediateSpacing(4);
1238 relPosFilter->IntermediateSpacingFlagOn();
1239 relPosFilter->SetFuzzyThreshold(0.9); // remove few part only
1240 relPosFilter->RemoveObjectFlagOff();
1241 relPosFilter->Update();
1242 m_Working_Support = relPosFilter->GetOutput();
1244 // Get a single 3D CCL
1245 m_Working_Support = Labelize<MaskImageType>(m_Working_Support, 0, false, 10);
1246 m_Working_Support = KeepLabels<MaskImageType>(m_Working_Support,
1247 GetBackgroundValue(),
1248 GetForegroundValue(), 1, 1, true);
1252 // Re-Add post to Esophagus -> sometimes previous relpos remove some
1253 // correct part below esophagus.
1254 MaskImagePointer Esophagus = GetAFDB()->template GetImage<MaskImageType>("Esophagus");
1255 EnlargeEsophagusDilatationRadiusInferiorly(Esophagus);
1256 writeImage<MaskImageType>(Esophagus, "e-again.mhd");
1257 typedef clitk::SliceBySliceRelativePositionFilter<MaskImageType> RelPosFilterType;
1258 typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
1259 relPosFilter->VerboseStepOff();
1260 relPosFilter->WriteStepOff();
1261 relPosFilter->SetInput(m_Working_Support);
1262 relPosFilter->SetInputObject(Esophagus);
1263 relPosFilter->SetOrientationTypeString("P");
1264 relPosFilter->InverseOrientationFlagOff();
1265 relPosFilter->SetDirection(2); // Z axis
1266 relPosFilter->UniqueConnectedComponentBySliceOff(); // important
1267 relPosFilter->SetIntermediateSpacing(4);
1268 relPosFilter->IntermediateSpacingFlagOn();
1269 relPosFilter->CombineWithOrFlagOn();
1270 relPosFilter->SetFuzzyThreshold(0.9); // remove few part only
1271 relPosFilter->RemoveObjectFlagOff();
1272 relPosFilter->Update();
1273 m_Working_Support = relPosFilter->GetOutput();
1276 StopCurrentStep<MaskImageType>(m_Working_Support);
1277 m_ListOfStations["8"] = m_Working_Support;
1279 //--------------------------------------------------------------------
1282 //--------------------------------------------------------------------
1283 template <class TImageType>
1285 clitk::ExtractLymphStationsFilter<TImageType>::
1286 FindExtremaPointsInBronchus(MaskImagePointer input,
1288 double distance_max_from_center_point,
1289 ListOfPointsType & LR,
1290 ListOfPointsType & Ant,
1291 ListOfPointsType & Post)
1294 // Other solution ==> with auto bounding box ! (but pb to prevent to
1295 // be too distant from the center point
1298 std::vector<typename MaskSliceType::Pointer> slices;
1299 clitk::ExtractSlices<MaskImageType>(input, 2, slices);
1303 for(uint i=0; i<slices.size(); i++) {
1306 slices[i] = Labelize<MaskSliceType>(slices[i], 0, true, 10);
1307 slices[i] = KeepLabels<MaskSliceType>(slices[i],
1308 GetBackgroundValue(),
1309 GetForegroundValue(), 1, 1, true);
1312 // ------- Find rightmost or leftmost point -------
1313 MaskSliceType::PointType LRMost;
1315 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i],
1316 GetBackgroundValue(),
1318 (direction==0?false:true), // right or left according to direction
1320 // ------- Find postmost point -------
1321 MaskSliceType::PointType postMost;
1323 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i],
1324 GetBackgroundValue(),
1326 distance_max_from_center_point,
1328 // ------- Find antmost point -------
1329 MaskSliceType::PointType antMost;
1331 clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices[i],
1332 GetBackgroundValue(),
1334 distance_max_from_center_point,
1336 // Only add point if found
1338 // ------- Convert 2D to 3D points --------
1339 MaskImageType::PointType p;
1340 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(LRMost, input, i, p);
1342 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(antMost, input, i, p);
1344 clitk::PointsUtils<MaskImageType>::Convert2DTo3D(postMost, input, i, p);
1349 //--------------------------------------------------------------------