SetFuzzyThreshold("7", "RightPulmonaryArtery", 0.3);
SetFuzzyThreshold("7", "LeftPulmonaryArtery", 0.5);
SetFuzzyThreshold("7", "SVC", 0.2);
+ SetS7_UseMostInferiorPartOnlyFlag(false);
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template <class TImageType>
+void
+clitk::ExtractLymphStationsFilter<TImageType>::
+ExtractStation_7() {
+ if (CheckForStation("7")) {
+ ExtractStation_7_SI_Limits();
+ ExtractStation_7_RL_Interior_Limits();
+
+ // ExtractStation_7_Posterior_Limits();
+ ExtractStation_8_Single_CCL_Limits();
+ ExtractStation_7_Remove_Structures();
+ // Store image filenames into AFDB
+ writeImage<MaskImageType>(m_ListOfStations["7"], "seg/Station7.mhd");
+ GetAFDB()->SetImageFilename("Station7", "seg/Station7.mhd");
+ WriteAFDB();
+ }
}
//--------------------------------------------------------------------
clitk::ExtractLymphStationsFilter<TImageType>::
ExtractStation_7_SI_Limits()
{
- StartNewStep("[Station7] Inf/Sup mediastinum limits with carina/LLLBronchus");
+ StartNewStep("[Station7] Inf/Sup mediastinum limits with carina/LLL-RMLBronchus");
// Get Inputs
MaskImagePointer Trachea = GetAFDB()->template GetImage <MaskImageType>("Trachea");
+
+ // Create line from A to B with
+ // A = upper border of LLL at left
+ // B = lower border of bronchus intermedius (BI) or RightMiddleLobeBronchus
+ ImagePointType A;
+ ImagePointType B;
+ FindLineForS7S8Separation(A, B);
+
+ // // if option -> replace A[0] with B[0]
+ // if (GetS7_UseMostInferiorPartOnlyFlag()) {
+ // A[0] = B[0];
+ // }
+
+ // Use line to remove the inferior part
+ m_Working_Support =
+ SliceBySliceSetBackgroundFromSingleLine<MaskImageType>(m_Working_Support, GetBackgroundValue(),
+ A, B, 2, 0, false);
+
+ // Get the CarinaZ position
+ m_CarinaZ = FindCarinaSlicePosition();
- // We suppoe that CarinaZ was already computed (S8)
- double m_CarinaZ = GetAFDB()->GetDouble("CarinaZ");
+ // Crop support
+ m_Working_Support =
+ clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
+ A[2], m_CarinaZ, true,
+ GetBackgroundValue());
+ // Crop trachea
+ m_Working_Trachea =
+ clitk::CropImageAlongOneAxis<MaskImageType>(Trachea, 2,
+ A[2], m_CarinaZ, true,
+ GetBackgroundValue());
- // double m_OriginOfRightMiddleLobeBronchusZ = GetAFDB()->GetPoint3D("OriginOfRightMiddleLobeBronchus", 2);
- // DD(m_OriginOfRightMiddleLobeBronchusZ);
- MaskImagePointer UpperBorderOfLLLBronchus = GetAFDB()->template GetImage<MaskImageType>("UpperBorderOfLLLBronchus");
+ StopCurrentStep<MaskImageType>(m_Working_Support);
+ m_ListOfStations["7"] = m_Working_Support;
+}
+//--------------------------------------------------------------------
- // Search most inf point (WHY ? IS IT THE RIGHT STRUCTURE ??)
- MaskImagePointType ps = UpperBorderOfLLLBronchus->GetOrigin(); // initialise to avoid warning
- clitk::FindExtremaPointInAGivenDirection<MaskImageType>(UpperBorderOfLLLBronchus, GetBackgroundValue(), 2, true, ps);
- double m_UpperBorderOfLLLBronchusZ = ps[2];
+//--------------------------------------------------------------------
+template <class TImageType>
+void
+clitk::ExtractLymphStationsFilter<TImageType>::
+ExtractStation_7_RL_Interior_Limits()
+{
+ // ----------------------------------------------------------------
+ StartNewStep("[Station7] RL limits with bronchi");
+
/*
- std::vector<MaskImagePointType> centroids;
- clitk::ComputeCentroids<MaskImageType>(UpperBorderOfLLLBronchus, GetBackgroundValue(), centroids);
- double m_UpperBorderOfLLLBronchusZ = centroids[1][2];
- DD(m_UpperBorderOfLLLBronchusZ)
+ Slice by Slice, consider most Left point of the Right
+ bronchus. Remove the Ant/Right corner
*/
- /* Crop support */
+ // First consider bronchi
+ FindLeftAndRightBronchi();
+ m_RightBronchus = GetAFDB()->template GetImage <MaskImageType>("RightBronchus");
+ m_LeftBronchus = GetAFDB()->template GetImage <MaskImageType>("LeftBronchus");
+
+ // Resize like m_Working_Support
+ m_LeftBronchus =
+ clitk::ResizeImageLike<MaskImageType>(m_LeftBronchus, m_Working_Support, GetBackgroundValue());
+ m_RightBronchus =
+ clitk::ResizeImageLike<MaskImageType>(m_RightBronchus, m_Working_Support, GetBackgroundValue());
+
+ // Extract slices, Label, compute centroid, keep most central connected component
+ std::vector<MaskSlicePointer> slices_leftbronchus;
+ std::vector<MaskSlicePointer> slices_rightbronchus;
+ std::vector<MaskSlicePointer> slices_support;
+ clitk::ExtractSlices<MaskImageType>(m_LeftBronchus, 2, slices_leftbronchus);
+ clitk::ExtractSlices<MaskImageType>(m_RightBronchus, 2, slices_rightbronchus);
+ clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices_support);
+
+ // Keep only the CCL of the bronchus with the closest to the center
+ // Loop on slices for left bronchus
+ for(uint i=0; i<slices_leftbronchus.size(); i++) {
+ slices_leftbronchus[i] = Labelize<MaskSliceType>(slices_leftbronchus[i], 0, false, 10);
+ std::vector<typename MaskSliceType::PointType> c;
+ clitk::ComputeCentroids<MaskSliceType>(slices_leftbronchus[i], GetBackgroundValue(), c);
+ if (c.size() > 1) {
+ double most_at_left = c[1][0];
+ int most_at_left_index=1;
+ for(uint j=1; j<c.size(); j++) {
+ if (c[j][0] < most_at_left) {
+ most_at_left = c[j][0];
+ most_at_left_index = j;
+ }
+ }
+ // Put all other CCL to Background
+ slices_leftbronchus[i] =
+ clitk::Binarize<MaskSliceType>(slices_leftbronchus[i], most_at_left_index,
+ most_at_left_index, GetBackgroundValue(), GetForegroundValue());
+ } // end c.size
+ }
+
+ // Loop on slices for right bronchus
+ for(uint i=0; i<slices_rightbronchus.size(); i++) {
+ slices_rightbronchus[i] = Labelize<MaskSliceType>(slices_rightbronchus[i], 0, false, 10);
+ std::vector<typename MaskSliceType::PointType> c;
+ clitk::ComputeCentroids<MaskSliceType>(slices_rightbronchus[i], GetBackgroundValue(), c);
+ if (c.size() > 1) {
+ double most_at_right = c[1][0];
+ int most_at_right_index=1;
+ for(uint j=1; j<c.size(); j++) {
+ if (c[j][0] > most_at_right) {
+ most_at_right = c[j][0];
+ most_at_right_index = j;
+ }
+ }
+ // Put all other CCL to Background
+ slices_rightbronchus[i] =
+ clitk::Binarize<MaskSliceType>(slices_rightbronchus[i], most_at_right_index,
+ most_at_right_index, GetBackgroundValue(), GetForegroundValue());
+ } // end c.size
+ }
+
+ // Joint slices
+ m_LeftBronchus = clitk::JoinSlices<MaskImageType>(slices_leftbronchus, m_LeftBronchus, 2);
+ m_RightBronchus = clitk::JoinSlices<MaskImageType>(slices_rightbronchus, m_RightBronchus, 2);
+
+ // For Right bronchus, Find most Left point. Remove corner Ant/Right corner
+ for(uint i=0; i<slices_rightbronchus.size(); i++) {
+ // Find most point most at left
+ MaskSlicePointType p_left;
+ MaskSlicePointType p_post;
+ bool b = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices_rightbronchus[i], GetBackgroundValue(), 0, false, p_left);
+ if (b) {
+ b = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices_rightbronchus[i], GetBackgroundValue(), 1, false, p_post);
+ }
+ if (b) {
+ MaskSlicePointType p = p_left;
+ p[1] = p_post[1];
+ MaskSliceIndexType pi;
+ slices_rightbronchus[i]->TransformPhysicalPointToIndex(p, pi);
+
+ // Build region to remove
+ MaskSliceRegionType region = slices_rightbronchus[i]->GetLargestPossibleRegion();
+ MaskSliceIndexType index = region.GetIndex();
+ MaskSliceSizeType size = region.GetSize();
+ size[0] = pi[0] - index[0];
+ size[1] = pi[1] - index[1];
+ region.SetSize(size);
+
+ // Fill region with Background value
+ clitk::FillRegionWithValue<MaskSliceType>(slices_support[i], GetBackgroundValue(), region);
+ }
+ }
+
+ // For Left bronchus, Find most Right point. Remove corner Ant/Left corner
+ for(uint i=0; i<slices_leftbronchus.size(); i++) {
+ // Find most point most at right
+ MaskSlicePointType p_right;
+ MaskSlicePointType p_post;
+ bool b = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices_leftbronchus[i], GetBackgroundValue(), 0, true, p_right);
+ if (b) {
+ b = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(slices_rightbronchus[i], GetBackgroundValue(), 1, false, p_post);
+ }
+ if (b) {
+ MaskSlicePointType p = p_right;
+ p[1] = p_post[1];
+ MaskSliceIndexType pi;
+ slices_leftbronchus[i]->TransformPhysicalPointToIndex(p, pi);
+
+ /* typedef itk::ImageRegionIterator<ImageType> IteratorType;
+ IteratorType iter(input, region);
+ iter.GoToBegin();
+ while (!iter.IsAtEnd()) {
+ MaskSliceIndexType index = iter.GetIndex();
+ if (index[0] > pi[0]) && (index[1] > pi[1]) iter.Set(GetBackgroundValue());
+
+ ++iter;
+ }
+ */
+
+
+ // Build region to remove
+ MaskSliceRegionType region = slices_leftbronchus[i]->GetLargestPossibleRegion();
+ MaskSliceIndexType index = region.GetIndex();
+ MaskSliceSizeType size = region.GetSize();
+ index[0] = pi[0];
+ size[0] = slices_leftbronchus[i]->GetLargestPossibleRegion().GetSize()[0] - pi[0];
+ size[1] = pi[1] - index[1];
+ region.SetSize(size);
+ region.SetIndex(index);
+
+ // Fill region with Background value
+ clitk::FillRegionWithValue<MaskSliceType>(slices_support[i], GetBackgroundValue(), region);
+ }
+ }
+
+ m_Working_Support = clitk::JoinSlices<MaskImageType>(slices_support, m_Working_Support, 2);
+
+ // Also remove what is at right of the Right bronchus (left respectively)
m_Working_Support =
- clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
- m_UpperBorderOfLLLBronchusZ,
- m_CarinaZ, true,
- GetBackgroundValue());
- /* Crop trachea */
- m_Working_Trachea =
- clitk::CropImageAlongOneAxis<MaskImageType>(Trachea, 2,
- m_UpperBorderOfLLLBronchusZ,
- m_CarinaZ, true,
- GetBackgroundValue());
+ clitk::SliceBySliceRelativePosition<MaskImageType>(m_Working_Support, m_LeftBronchus, 2,
+ GetFuzzyThreshold("7", "Bronchi"), "NotLeftTo",
+ false, 3, false);
+ m_Working_Support =
+ clitk::SliceBySliceRelativePosition<MaskImageType>(m_Working_Support, m_RightBronchus, 2,
+ GetFuzzyThreshold("7", "Bronchi"), "NotRightTo",
+ false, 3, false);
+
+ // SECOND PART
StopCurrentStep<MaskImageType>(m_Working_Support);
- m_ListOfStations["7"] = m_Working_Support;
}
//--------------------------------------------------------------------
template <class TImageType>
void
clitk::ExtractLymphStationsFilter<TImageType>::
-ExtractStation_7_RL_Limits()
+ExtractStation_7_RL_Limits_OLD()
{
// ----------------------------------------------------------------
StartNewStep("[Station7] Limits with bronchus : RightTo the left bronchus");
clitk::ExtractSlices<MaskImageType>(m_LeftBronchus, 2, slices_leftbronchus);
clitk::ExtractSlices<MaskImageType>(m_RightBronchus, 2, slices_rightbronchus);
+ // Version #1 with limit = centroid of the bronchus (OUTDATED)
+ // Step1 = keep only the CCL of the bronchus with the closest to the center
+ // Step2 = SliceBySlice Rel pos to both bronchi
+
// Loop on slices
for(uint i=0; i<slices_leftbronchus.size(); i++) {
slices_leftbronchus[i] = Labelize<MaskSliceType>(slices_leftbronchus[i], 0, false, 10);
Remove_Structures("7", "AzygousVein");
Remove_Structures("7", "Aorta");
- Remove_Structures("7", "Esophagus");
Remove_Structures("7", "RightPulmonaryArtery");
Remove_Structures("7", "LeftPulmonaryArtery");
Remove_Structures("7", "LeftSuperiorPulmonaryVein");
Remove_Structures("7", "PulmonaryTrunk");
Remove_Structures("7", "VertebralBody");
+ // Keep only one CCL by slice (before removing Esophagus)
+ // DD("SliceBySliceKeepMainCCL");
+
+ // TODO -> replace by keep the one that contains point at the middle of the line between the bronchus
+ // -> new function "keep/select" the ccl that contains this point (2D)
+
+ //m_Working_Support = clitk::SliceBySliceKeepMainCCL<MaskImageType>(m_Working_Support, GetBackgroundValue(), GetForegroundValue());
+
+ Remove_Structures("7", "Esophagus");
+
// END
StopCurrentStep<MaskImageType>(m_Working_Support);
m_ListOfStations["7"] = m_Working_Support;
clitk::ExtractLymphStationsFilter<ImageType>::
ExtractStation_8_SetDefaultValues()
{
- SetDistanceMaxToAnteriorPartOfTheSpine(10);
MaskImagePointType p;
p[0] = 15; p[1] = 2; p[2] = 1;
SetEsophagusDiltationForAnt(p);
}
//--------------------------------------------------------------------
+//--------------------------------------------------------------------
+template <class TImageType>
+void
+clitk::ExtractLymphStationsFilter<TImageType>::
+ExtractStation_8()
+{
+ if (CheckForStation("8")) {
+ ExtractStation_8_SI_Limits(); // OK, validated
+ ExtractStation_8_Ant_Limits(); // OK, validated
+ ExtractStation_8_Left_Sup_Limits(); // OK, validated
+ ExtractStation_8_Left_Inf_Limits(); // OK, validated
+ ExtractStation_8_Single_CCL_Limits(); // OK, validated
+ ExtractStation_8_Remove_Structures(); // OK, validated
+
+ // Store image filenames into AFDB
+ writeImage<MaskImageType>(m_ListOfStations["8"], "seg/Station8.mhd");
+ GetAFDB()->SetImageFilename("Station8", "seg/Station8.mhd");
+ WriteAFDB();
+ }
+}
+//--------------------------------------------------------------------
+
+
//--------------------------------------------------------------------
template <class ImageType>
void
left; the lower border of the bronchus intermedius on the right"
*/
- StartNewStep("[Station8] Inf/Sup mediastinum limits with carina/diaphragm junction");
-
- // Get Carina Z position
- MaskImagePointer Carina = GetAFDB()->template GetImage<MaskImageType>("Carina");
+ StartNewStep("[Station8] Sup/Inf limits with LeftLower/RightMiddle Lobe and diaphragm");
- std::vector<MaskImagePointType> centroids;
- clitk::ComputeCentroids<MaskImageType>(Carina, GetBackgroundValue(), centroids);
- m_CarinaZ = centroids[1][2];
- // DD(m_CarinaZ);
- // add one slice to include carina ?
- m_CarinaZ += m_Mediastinum->GetSpacing()[2];
- // We dont need Carina structure from now
- Carina->Delete();
- GetAFDB()->SetDouble("CarinaZ", m_CarinaZ);
+ /* -----------------------------------------------
+ NEW SUPERIOR LIMIT = LeftLowerLobeBronchus /
+ RightMiddleLobeBronchus See FindLineForS7S8Separation
+ -----------------------------------------------
+ */
+ ImagePointType A;
+ ImagePointType B;
+ FindLineForS7S8Separation(A, B);
+
+ // add one slice to be adjacent to Station7
+ B[2] += m_Working_Support->GetSpacing()[2];
+ A[2] += m_Working_Support->GetSpacing()[2];
+
+ // Use the line to remove the inferior part
+ m_Working_Support =
+ SliceBySliceSetBackgroundFromSingleLine<MaskImageType>(m_Working_Support,
+ GetBackgroundValue(), A, B, 2, 0, true);
+ /* -----------------------------------------------
+ INFERIOR LIMIT = Diaphragm
+ -----------------------------------------------
+ */
+
// Found most inferior part of the lung
MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
// It should be already croped, so I took the origin and add 10mm above
m_DiaphragmInferiorLimit = Lungs->GetOrigin()[2]+10;
- // Lungs->Delete(); // we don't need it, release memory -> it we want to release, also free in AFDB
- clitk::PrintMemory(GetVerboseMemoryFlag(), "after reading lungs");
GetAFDB()->template ReleaseImage<MaskImageType>("Lungs");
- clitk::PrintMemory(GetVerboseMemoryFlag(), "after release lungs");
- /* Crop support :
- Superior limit = carina
- Inferior limit = DiaphragmInferiorLimit (old=Gastroesphogeal junction) */
m_Working_Support =
clitk::CropImageAlongOneAxis<MaskImageType>(m_Working_Support, 2,
- m_DiaphragmInferiorLimit, //GOjunctionZ,
- m_CarinaZ, true,
+ m_DiaphragmInferiorLimit,
+ B[2], true,
GetBackgroundValue());
-
- // Remove some structures that we know are excluded
- MaskImagePointer VertebralBody =
- GetAFDB()->template GetImage <MaskImageType>("VertebralBody");
- MaskImagePointer Aorta =
- GetAFDB()->template GetImage <MaskImageType>("Aorta");
-
- typedef clitk::BooleanOperatorLabelImageFilter<MaskImageType> BoolFilterType;
- typename BoolFilterType::Pointer boolFilter = BoolFilterType::New();
- boolFilter->InPlaceOn();
- boolFilter->SetInput1(m_Working_Support);
- boolFilter->SetInput2(VertebralBody);
- boolFilter->SetOperationType(BoolFilterType::AndNot);
- boolFilter->Update();
- /*
-
- DO NOT REMOVE AORTA YET (LATER)
-
- boolFilter->SetInput1(boolFilter->GetOutput());
- boolFilter->SetInput2(Aorta);
- boolFilter->SetOperationType(BoolFilterType::AndNot);
- boolFilter->Update();
- */
- m_Working_Support = boolFilter->GetOutput();
-
// Done.
StopCurrentStep<MaskImageType>(m_Working_Support);
m_ListOfStations["8"] = m_Working_Support;
template <class ImageType>
void
clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_Post_Limits()
-{
- /*
- Station 8: paraeosphageal nodes
-
- Anteriorly, it is in contact with Station 7 and the
- left main stem bronchus in its superior aspect (Fig. 3C–H) and
- with the heart more inferiorly. Posteriorly, Station 8 abuts the
- descending aorta and the anterior aspect of the vertebral body
- until an imaginary horizontal line running 1 cm posterior to the
- anterior border of the vertebral body (Fig. 3C).
-
- New classification IASCL 2009 :
-
- "Nodes lying adjacent to the wall of the esophagus and to the
- right or left of the midline, excluding subcarinal nodes (S7)
- Upper"
-
- */
-
- StartNewStep("[Station8] Post limits with vertebral body");
- MaskImagePointer VertebralBody =
- GetAFDB()->template GetImage <MaskImageType>("VertebralBody");
-
- // Consider vertebral body slice by slice
- typedef clitk::ExtractSliceFilter<MaskImageType> ExtractSliceFilterType;
- typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
- typedef typename ExtractSliceFilterType::SliceType SliceType;
- std::vector<typename SliceType::Pointer> vertebralSlices;
- extractSliceFilter->SetInput(VertebralBody);
- extractSliceFilter->SetDirection(2);
- extractSliceFilter->Update();
- extractSliceFilter->GetOutputSlices(vertebralSlices);
-
- // For each slice, compute the most anterior point
- std::map<int, typename SliceType::PointType> vertebralAntPositionBySlice;
- for(uint i=0; i<vertebralSlices.size(); i++) {
- typename SliceType::PointType p;
- bool found = clitk::FindExtremaPointInAGivenDirection<SliceType>(vertebralSlices[i],
- GetBackgroundValue(),
- 1, true, p);
- if (found) {
- vertebralAntPositionBySlice[i] = p;
- }
- else {
- // no Foreground in this slice (it appends generally at the
- // beginning and the end of the volume). Do nothing in this
- // case.
- }
- }
-
- // Convert 2D points in slice into 3D points
- std::vector<MaskImagePointType> vertebralAntPositions;
- clitk::PointsUtils<MaskImageType>::Convert2DMapTo3DList(vertebralAntPositionBySlice,
- VertebralBody,
- vertebralAntPositions);
-
- // DEBUG : write list of points
- clitk::WriteListOfLandmarks<MaskImageType>(vertebralAntPositions,
- "S8-vertebralMostAntPositions-points.txt");
-
- // Cut support posteriorly 1cm the most anterior point of the
- // VertebralBody. Do nothing below and above the VertebralBody. To
- // do that compute several region, slice by slice and fill.
- MaskImageRegionType region;
- MaskImageSizeType size;
- MaskImageIndexType start;
- size[2] = 1; // a single slice
- start[0] = m_Working_Support->GetLargestPossibleRegion().GetIndex()[0];
- size[0] = m_Working_Support->GetLargestPossibleRegion().GetSize()[0];
- for(uint i=0; i<vertebralAntPositions.size(); i++) {
- typedef typename itk::ImageRegionIterator<MaskImageType> IteratorType;
- IteratorType iter =
- IteratorType(m_Working_Support, m_Working_Support->GetLargestPossibleRegion());
- MaskImageIndexType index;
- // Consider some cm posterior to most anterior positions (usually
- // 1 cm).
- vertebralAntPositions[i][1] += GetDistanceMaxToAnteriorPartOfTheSpine();
- // Get index of this point
- m_Working_Support->TransformPhysicalPointToIndex(vertebralAntPositions[i], index);
- // Compute region (a single slice)
- start[2] = index[2];
- start[1] = m_Working_Support->GetLargestPossibleRegion().GetIndex()[1]+index[1];
- size[1] = m_Working_Support->GetLargestPossibleRegion().GetSize()[1]-start[1];
- region.SetSize(size);
- region.SetIndex(start);
- // Fill region
- if (m_Working_Support->GetLargestPossibleRegion().IsInside(start)) {
- itk::ImageRegionIterator<MaskImageType> it(m_Working_Support, region);
- it.GoToBegin();
- while (!it.IsAtEnd()) {
- it.Set(GetBackgroundValue());
- ++it;
- }
- }
- }
-
- StopCurrentStep<MaskImageType>(m_Working_Support);
- m_ListOfStations["8"] = m_Working_Support;
-}
-//--------------------------------------------------------------------
-
-
-//--------------------------------------------------------------------
-template <class ImageType>
-void
-clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_Ant_Sup_Limits()
+ExtractStation_8_Ant_Limits()
{
- //--------------------------------------------------------------------
- StartNewStep("[Station8] Ant limits with S7 above Carina");
- /*
- Anteriorly, it is in contact with Station 7 and the
- left main stem bronchus in its superior aspect (Fig. 3C–H) and
- with the heart more inferiorly.
-
- 1) line post wall bronchi (S7), till originRMLB
- - LUL bronchus : to detect in trachea. But not needed here ??
- 2) heart ! -> not delineated.
- ==> below S7, crop CT not to far away (ant), then try with threshold
-
- 1) ==> how to share with S7 ? Prepare both support at the same time !
- NEED vector of initial support for each station ? No -> map if it exist before, take it !!
-
- */
-
- // Ant limit from carina (start) to end of S7 = originRMLB
- // Get Trachea
- MaskImagePointer Trachea = GetAFDB()->template GetImage<MaskImageType>("Trachea");
-
- MaskImagePointer m_Working_Trachea =
- clitk::CropImageRemoveGreaterThan<MaskImageType>(Trachea, 2, m_CarinaZ, true, // AutoCrop
- GetBackgroundValue());
-
- // Seprate into two main bronchi
- MaskImagePointer RightBronchus;
- MaskImagePointer LeftBronchus;
-
- // Labelize and consider the two first (main) labels
- m_Working_Trachea = Labelize<MaskImageType>(m_Working_Trachea, 0, true, 1);
-
- // Carina position must at the first slice that separate the two
- // main bronchus (not superiorly). We thus first check that the
- // upper slice is composed of at least two labels
- typedef itk::ImageSliceIteratorWithIndex<MaskImageType> SliceIteratorType;
- SliceIteratorType iter(m_Working_Trachea, m_Working_Trachea->GetLargestPossibleRegion());
- iter.SetFirstDirection(0);
- iter.SetSecondDirection(1);
- iter.GoToReverseBegin(); // Start from the end (because image is IS not SI)
- int maxLabel=0;
- while (!iter.IsAtReverseEndOfSlice()) {
- while (!iter.IsAtReverseEndOfLine()) {
- if (iter.Get() > maxLabel) maxLabel = iter.Get();
- --iter;
- }
- iter.PreviousLine();
- }
- if (maxLabel < 2) {
- clitkExceptionMacro("First slice form Carina does not seems to seperate the two main bronchus. Abort");
- }
-
- // Compute 3D centroids of both parts to identify the left from the
- // right bronchus
- std::vector<ImagePointType> c;
- clitk::ComputeCentroids<MaskImageType>(m_Working_Trachea, GetBackgroundValue(), c);
- ImagePointType C1 = c[1];
- ImagePointType C2 = c[2];
-
- ImagePixelType rightLabel;
- ImagePixelType leftLabel;
- if (C1[0] < C2[0]) { rightLabel = 1; leftLabel = 2; }
- else { rightLabel = 2; leftLabel = 1; }
-
- // Select LeftLabel (set one label to Backgroundvalue)
- RightBronchus =
- clitk::Binarize<MaskImageType>(m_Working_Trachea, rightLabel, rightLabel,
- GetBackgroundValue(), GetForegroundValue());
- /*
- SetBackground<MaskImageType, MaskImageType>(m_Working_Trachea, m_Working_Trachea,
- leftLabel, GetBackgroundValue(), false);
- */
- LeftBronchus = clitk::Binarize<MaskImageType>(m_Working_Trachea, leftLabel, leftLabel,
- GetBackgroundValue(), GetForegroundValue());
- /*
- SetBackground<MaskImageType, MaskImageType>(m_Working_Trachea, m_Working_Trachea,
- rightLabel, GetBackgroundValue(), false);
- */
-
- // Crop images
- RightBronchus = clitk::AutoCrop<MaskImageType>(RightBronchus, GetBackgroundValue());
- LeftBronchus = clitk::AutoCrop<MaskImageType>(LeftBronchus, GetBackgroundValue());
-
- // Insert int AFDB if need after
- GetAFDB()->template SetImage <MaskImageType>("RightBronchus", "seg/rightBronchus.mhd",
- RightBronchus, true);
- GetAFDB()->template SetImage <MaskImageType>("LeftBronchus", "seg/leftBronchus.mhd",
- LeftBronchus, true);
-
- // Now crop below OriginOfRightMiddleLobeBronchusZ
- // It is not done before to keep entire bronchi.
-
- MaskImagePointer OriginOfRightMiddleLobeBronchus =
- GetAFDB()->template GetImage<MaskImageType>("OriginOfRightMiddleLobeBronchus");
- std::vector<MaskImagePointType> centroids;
- clitk::ComputeCentroids<MaskImageType>(OriginOfRightMiddleLobeBronchus, GetBackgroundValue(), centroids);
- m_OriginOfRightMiddleLobeBronchusZ = centroids[1][2];
- // add one slice to include carina ?
- m_OriginOfRightMiddleLobeBronchusZ += RightBronchus->GetSpacing()[2];
- // We dont need Carina structure from now
- OriginOfRightMiddleLobeBronchus->Delete();
-
- RightBronchus =
- clitk::CropImageRemoveLowerThan<MaskImageType>(RightBronchus, 2,
- m_OriginOfRightMiddleLobeBronchusZ,
- true, // AutoCrop
- GetBackgroundValue());
- LeftBronchus =
- clitk::CropImageRemoveLowerThan<MaskImageType>(LeftBronchus, 2,
- m_OriginOfRightMiddleLobeBronchusZ,
- true, // AutoCrop
- GetBackgroundValue());
-
- // Search for points that are the most left/post/ant and most
- // right/post/ant of the left and right bronchus
- // 15 = not 15 mm more distance than the middle point.
- FindExtremaPointsInBronchus(RightBronchus, 0, 10, m_LeftMostInRightBronchus,
- m_AntMostInRightBronchus, m_PostMostInRightBronchus);
-
- FindExtremaPointsInBronchus(LeftBronchus, 1, 10, m_RightMostInLeftBronchus,
- m_AntMostInLeftBronchus, m_PostMostInLeftBronchus);
-
- // DEBUG : write the list of points
- ListOfPointsType v;
- v.reserve(m_LeftMostInRightBronchus.size()+m_AntMostInRightBronchus.size()+
- m_PostMostInRightBronchus.size());
- v.insert(v.end(), m_LeftMostInRightBronchus.begin(), m_LeftMostInRightBronchus.end() );
- v.insert(v.end(), m_AntMostInRightBronchus.begin(), m_AntMostInRightBronchus.end() );
- v.insert(v.end(), m_PostMostInRightBronchus.begin(), m_PostMostInRightBronchus.end() );
- clitk::WriteListOfLandmarks<MaskImageType>(v, "S8-RightBronchus-points.txt");
-
- v.clear();
- v.reserve(m_RightMostInLeftBronchus.size()+m_AntMostInLeftBronchus.size()+
- m_PostMostInLeftBronchus.size());
- v.insert(v.end(), m_RightMostInLeftBronchus.begin(), m_RightMostInLeftBronchus.end() );
- v.insert(v.end(), m_AntMostInLeftBronchus.begin(), m_AntMostInLeftBronchus.end() );
- v.insert(v.end(), m_PostMostInLeftBronchus.begin(), m_PostMostInLeftBronchus.end() );
- clitk::WriteListOfLandmarks<MaskImageType>(v, "S8-LeftBronchus-points.txt");
-
- v.clear();
- v.reserve(m_PostMostInLeftBronchus.size()+m_PostMostInRightBronchus.size());
- v.insert(v.end(), m_PostMostInLeftBronchus.begin(), m_PostMostInLeftBronchus.end() );
- v.insert(v.end(), m_PostMostInRightBronchus.begin(), m_PostMostInRightBronchus.end() );
- clitk::WriteListOfLandmarks<MaskImageType>(v, "S8-RightLeftBronchus-points.txt");
-
-
- // Now uses these points to limit, slice by slice
- // line is mainly horizontal, so mainDirection=1
- clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
- m_PostMostInRightBronchus,
- m_PostMostInLeftBronchus,
- GetBackgroundValue(), 1, 10);
-
- // Keep main 3D CCL :
- m_Working_Support = Labelize<MaskImageType>(m_Working_Support, 0, false, 10);
- m_Working_Support = KeepLabels<MaskImageType>(m_Working_Support,
- GetBackgroundValue(),
- GetForegroundValue(), 1, 1, true);
-
- // Autocrop
- m_Working_Support = clitk::AutoCrop<MaskImageType>(m_Working_Support, GetBackgroundValue());
-
- // End of step
- StopCurrentStep<MaskImageType>(m_Working_Support);
- // m_ListOfStations["8"] = m_Working_Support;
-
-}
-
-//--------------------------------------------------------------------
-template <class ImageType>
-void
-clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_Ant_Inf_Limits()
-{
-
//--------------------------------------------------------------------
StartNewStep("[Station8] Ant part: not post to Esophagus");
/*
// Get Esophagus
m_Esophagus = GetAFDB()->template GetImage<MaskImageType>("Esophagus");
- clitk::PrintMemory(GetVerboseMemoryFlag(), "after read Esophagus");
// In images from the original article, Atlas – UM, the oesophagus
//was included in nodal stations 3p and 8. Having said that, in the
//prospective, the oesophagus should be excluded from these nodal
//stations.
- /* NOT YET !! DO IT LATER
-
- typedef clitk::BooleanOperatorLabelImageFilter<MaskImageType> BoolFilterType;
- typename BoolFilterType::Pointer boolFilter = BoolFilterType::New();
- boolFilter->InPlaceOn();
- boolFilter->SetInput1(m_Working_Support);
- boolFilter->SetInput2(m_Esophagus);
- boolFilter->SetOperationType(BoolFilterType::AndNot);
- boolFilter->Update();
- m_Working_Support = boolFilter->GetOutput();
-
- */
-
- // Crop Esophagus : keep only below the OriginOfRightMiddleLobeBronchusZ
+ // Resize Esophagus like current support
m_Esophagus =
- clitk::CropImageRemoveGreaterThan<MaskImageType>(m_Esophagus, 2,
- m_OriginOfRightMiddleLobeBronchusZ,
- true, // AutoCrop
- GetBackgroundValue());
+ clitk::ResizeImageLike<MaskImageType>(m_Esophagus, m_Working_Support, GetBackgroundValue()); // Needed ?
// Dilate to keep only not Anterior positions
MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt();
-
- // m_Esophagus = EnlargeEsophagusDilatationRadiusInferiorly(m_Esophagus);
-
m_Esophagus = clitk::Dilate<MaskImageType>(m_Esophagus,
radiusInMM,
GetBackgroundValue(),
GetForegroundValue(), true);
-
- // Remove Anterior part according to this dilatated esophagus. Note:
- // because we crop Esophagus with ORML, the support will also be
- // croped in the same way. Here it is a desired feature. If we dont
- // want, use SetIgnoreEmptySliceObject(true)
-
- // In the new IASCL definition, it is not clear if sup limits is
- // around carina or On the right, it is “the lower border of the
- // bronchus intermedius”, indicated on the image set as a point
- // (“lower border of the bronchus intermedius”)
-
+ // Keep what is Posterior to Esophagus
typedef clitk::SliceBySliceRelativePositionFilter<MaskImageType> RelPosFilterType;
typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
relPosFilter->VerboseStepFlagOff();
template <class ImageType>
void
clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_Ant_Injected_Limits()
+ExtractStation_8_Left_Sup_Limits()
{
-
//--------------------------------------------------------------------
- StartNewStep("[Station8] Ant part (remove high density, injected part)");
-
- // Consider initial image, crop to current support
- ImagePointer working_input =
- clitk::ResizeImageLike<ImageType>(m_Input,
- m_Working_Support,
- (short)GetBackgroundValue());
-
- // Threshold
- typedef itk::BinaryThresholdImageFilter<ImageType, MaskImageType> BinarizeFilterType;
- typename BinarizeFilterType::Pointer binarizeFilter = BinarizeFilterType::New();
- binarizeFilter->SetInput(working_input);
- binarizeFilter->SetLowerThreshold(GetInjectedThresholdForS8());
- binarizeFilter->SetInsideValue(GetForegroundValue());
- binarizeFilter->SetOutsideValue(GetBackgroundValue());
- binarizeFilter->Update();
- MaskImagePointer injected = binarizeFilter->GetOutput();
-
- // Combine with current support
- clitk::AndNot<MaskImageType>(m_Working_Support, injected, GetBackgroundValue());
-
- StopCurrentStep<MaskImageType>(m_Working_Support);
- m_ListOfStations["8"] = m_Working_Support;
-}
-//--------------------------------------------------------------------
-
-
-
-//--------------------------------------------------------------------
-template <class ImageType>
-void
-clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_LR_1_Limits()
-{
- //--------------------------------------------------------------------
- StartNewStep("[Station8] Left and Right (from Carina to PulmonaryTrunk): Right to LeftPulmonaryArtery");
-
- /*
- We remove LeftPulmonaryArtery structure and what is at Left to
- this structure.
- */
- MaskImagePointer LeftPulmonaryArtery = GetAFDB()->template GetImage<MaskImageType>("LeftPulmonaryArtery");
-
- // Relative Position : not at Left
- typedef clitk::AddRelativePositionConstraintToLabelImageFilter<MaskImageType> RelPosFilterType;
- typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
- relPosFilter->VerboseStepFlagOff();
- relPosFilter->WriteStepFlagOff();
- relPosFilter->SetBackgroundValue(GetBackgroundValue());
- relPosFilter->SetInput(m_Working_Support);
- relPosFilter->SetInputObject(LeftPulmonaryArtery);
- relPosFilter->RemoveObjectFlagOn(); // remove the object too
- relPosFilter->AddOrientationTypeString("L");
- relPosFilter->InverseOrientationFlagOn(); // Not at Left
- relPosFilter->SetIntermediateSpacing(3);
- relPosFilter->IntermediateSpacingFlagOn();
- relPosFilter->SetFuzzyThreshold(0.7);
- relPosFilter->AutoCropFlagOn();
- relPosFilter->Update();
- m_Working_Support = relPosFilter->GetOutput();
-
- // Release LeftPulmonaryArtery
- GetAFDB()->template ReleaseImage<MaskImageType>("LeftPulmonaryArtery");
-
- StopCurrentStep<MaskImageType>(m_Working_Support);
- m_ListOfStations["8"] = m_Working_Support;
-}
-//--------------------------------------------------------------------
-
-
-//--------------------------------------------------------------------
-template <class ImageType>
-void
-clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_LR_2_Limits()
-{
- //--------------------------------------------------------------------
- StartNewStep("[Station8] Left and Right (from PulmTrunk to OriginMiddleLobeBronchus) Right to line from Aorta to PulmonaryTrunk");
+ StartNewStep("[Station8] Left limits: remove Left to line from Aorta to PulmonaryTrunk");
/*
We consider a line from Left part of Aorta to left part of
template <class ImageType>
void
clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_LR_Limits_old2()
-{
-
- //--------------------------------------------------------------------
- StartNewStep("[Station8] Left and Right limits arround esophagus (below Carina)");
-
- // Estract slices for current support for slice by slice processing
- std::vector<typename MaskSliceType::Pointer> slices;
- clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices);
-
- // Dilate the Esophagus to consider a margins around
- MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt();
- m_Esophagus = clitk::Dilate<MaskImageType>(m_Esophagus,
- radiusInMM,
- GetBackgroundValue(),
- GetForegroundValue(), true);
-
- // Remove what is outside the mediastinum in this enlarged Esophagus -> it allows to select
- // 'better' extrema points (not too post).
- MaskImagePointer Lungs = GetAFDB()->template GetImage<MaskImageType>("Lungs");
- clitk::AndNot<MaskImageType>(m_Esophagus, Lungs, GetBackgroundValue());
- GetAFDB()->template ReleaseImage<MaskImageType>("Lungs");
-
- // Estract slices of Esophagus (resize like support before to have the same set of slices)
- MaskImagePointer EsophagusForSlice = clitk::ResizeImageLike<MaskImageType>(m_Esophagus, m_Working_Support, GetBackgroundValue());
-
- std::vector<typename MaskSliceType::Pointer> eso_slices;
- clitk::ExtractSlices<MaskImageType>(EsophagusForSlice, 2, eso_slices);
-
- // Estract slices of Vertebral (resize like support before to have the same set of slices)
- MaskImagePointer VertebralBody = GetAFDB()->template GetImage<MaskImageType>("VertebralBody");
- VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
- std::vector<typename MaskSliceType::Pointer> vert_slices;
- clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, vert_slices);
-
- // Estract slices of Aorta (resize like support before to have the same set of slices)
- MaskImagePointer Aorta = GetAFDB()->template GetImage<MaskImageType>("Aorta");
- Aorta = clitk::ResizeImageLike<MaskImageType>(Aorta, m_Working_Support, GetBackgroundValue());
- std::vector<typename MaskSliceType::Pointer> aorta_slices;
- clitk::ExtractSlices<MaskImageType>(Aorta, 2, aorta_slices);
-
- // Extract slices of Mediastinum (resize like support before to have the same set of slices)
- m_Mediastinum = GetAFDB()->template GetImage<MaskImageType>("Mediastinum");
- m_Mediastinum = clitk::ResizeImageLike<MaskImageType>(m_Mediastinum, m_Working_Support, GetBackgroundValue());
- std::vector<typename MaskSliceType::Pointer> mediast_slices;
- clitk::ExtractSlices<MaskImageType>(m_Mediastinum, 2, mediast_slices);
-
- // List of points
- std::vector<MaskImagePointType> p_RightMostAnt;
- std::vector<MaskImagePointType> p_RightMostPost;
- std::vector<MaskImagePointType> p_LeftMostAnt;
- std::vector<MaskImagePointType> p_LeftMostPost;
- std::vector<MaskImagePointType> p_AllPoints;
- std::vector<MaskImagePointType> p_LeftAorta;
- std::vector<MaskImagePointType> p_LeftEso;
-
- /*
- In the following, we search for the LeftRight limits. We search
- for the most Right points in Esophagus and in VertebralBody and
- consider a line between those to most right points. All points in
- the support which are most right to this line are discarded. Same
- for the left part. The underlying assumption is that the support
- is concave between Eso/VertebralBody. Esophagus is a bit
- dilatated. On VertebralBody we go right (or left) until we reach
- the lung (but no more 20 mm).
- */
-
- // Loop slices
- MaskImagePointType p;
- MaskImagePointType pp;
- for(uint i=0; i<slices.size() ; i++) {
- // Declare all needed points (sp = slice point)
- typename MaskSliceType::PointType sp_maxRight_Eso;
- typename MaskSliceType::PointType sp_maxRight_Aorta;
- typename MaskSliceType::PointType sp_maxRight_Vertebra;
- typename MaskSliceType::PointType sp_maxLeft_Eso;
- typename MaskSliceType::PointType sp_maxLeft_Aorta;
- typename MaskSliceType::PointType sp_maxLeft_Vertebra;
-
- // Right is at left on screen, coordinate decrease
- // Left is at right on screen, coordinate increase
-
- // Find limit of Vertebral -> only at most Post part of current
- // slice support. First found most ant point in VertebralBody
- typedef MaskSliceType SliceType;
- typename SliceType::PointType p_slice_ant;
- bool found = clitk::FindExtremaPointInAGivenDirection<SliceType>(vert_slices[i], GetBackgroundValue(), 1, true, p_slice_ant);
- if (!found) {
- // It should not happen ! But sometimes, a contour is missing or
- // the VertebralBody is not delineated enough inferiorly ... in
- // those cases, we consider the first found slice.
- std::cerr << "No foreground pixels in this VertebralBody slices !?? I try with the previous/next slice" << std::endl;
- int j=i++;
- bool found = false;
- while (!found) {
- found = clitk::FindExtremaPointInAGivenDirection<SliceType>(vert_slices[j], GetBackgroundValue(), 1, true, p_slice_ant);
- //clitkExceptionMacro("No foreground pixels in this VertebralBody slices ??");
- j++;
- }
- }
- p_slice_ant[1] += GetDistanceMaxToAnteriorPartOfTheSpine(); // Consider offset
-
- // The, find most Right and Left points on that AP position
- typename SliceType::IndexType indexR;
- typename SliceType::IndexType indexL;
- vert_slices[i]->TransformPhysicalPointToIndex(p_slice_ant, indexR);
- indexL = indexR;
- // Check that is inside the mask
- indexR[1] = std::min(indexR[1], (long)vert_slices[i]->GetLargestPossibleRegion().GetSize()[1]-1);
- indexL[1] = indexR[1];
- while (vert_slices[i]->GetPixel(indexR) != GetBackgroundValue()) {
- indexR[0] --; // Go to the right
- }
- while (vert_slices[i]->GetPixel(indexL) != GetBackgroundValue()) {
- indexL[0] ++; // Go to the left
- }
- vert_slices[i]->TransformIndexToPhysicalPoint(indexR, sp_maxRight_Vertebra);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Vertebra, VertebralBody, i, p);
- p_AllPoints.push_back(p);
- vert_slices[i]->TransformIndexToPhysicalPoint(indexL, sp_maxLeft_Vertebra);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Vertebra, VertebralBody, i, p);
- p_AllPoints.push_back(p);
-
- // Find last point out of the mediastinum on this line, Right :
- mediast_slices[i]->TransformPhysicalPointToIndex(sp_maxRight_Vertebra, indexR);
- double distance = 0.0;
- while (mediast_slices[i]->GetPixel(indexR) != GetBackgroundValue()) {
- indexR[0] --;
- distance += mediast_slices[i]->GetSpacing()[0];
- }
- if (distance < 30) { // Ok in this case, we found limit with lung
- mediast_slices[i]->TransformIndexToPhysicalPoint(indexR, sp_maxRight_Vertebra);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Vertebra, m_Mediastinum, i, p);
- }
- else { // in that case, we are probably below the diaphragm, so we
- // add aribtrarly few mm
- sp_maxRight_Vertebra[0] -= 2; // Leave 2 mm around the VertebralBody
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Vertebra, m_Mediastinum, i, p);
- }
- p_RightMostPost.push_back(p);
- p_AllPoints.push_back(p);
-
- // Find last point out of the mediastinum on this line, Left :
- mediast_slices[i]->TransformPhysicalPointToIndex(sp_maxLeft_Vertebra, indexL);
- distance = 0.0;
- while (mediast_slices[i]->GetPixel(indexL) != GetBackgroundValue()) {
- indexL[0] ++;
- distance += mediast_slices[i]->GetSpacing()[0];
- }
- if (distance < 30) { // Ok in this case, we found limit with lung
- mediast_slices[i]->TransformIndexToPhysicalPoint(indexL, sp_maxLeft_Vertebra);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Vertebra, m_Mediastinum, i, p);
- }
- else { // in that case, we are probably below the diaphragm, so we
- // add aribtrarly few mm
- sp_maxLeft_Vertebra[0] += 2; // Leave 2 mm around the VertebralBody
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Vertebra, m_Mediastinum, i, p);
- }
- p_LeftMostPost.push_back(p);
- p_AllPoints.push_back(p);
-
- // Find Eso slice centroid and do not consider what is post to
- // this centroid.
- std::vector<typename MaskSliceType::PointType> c;
- clitk::ComputeCentroids<MaskSliceType>(eso_slices[i], GetBackgroundValue(), c);
- if (c.size() >1) {
- eso_slices[i] =
- clitk::CropImageRemoveGreaterThan<MaskSliceType>(eso_slices[i], 1, c[1][1], false, GetBackgroundValue());
- eso_slices[i] =
- clitk::ResizeImageLike<MaskSliceType>(eso_slices[i], aorta_slices[i], GetBackgroundValue());
- // writeImage<MaskSliceType>(eso_slices[i], "eso-slice-"+toString(i)+".mhd");
- }
-
- // Find right limit of Esophagus and Aorta
- bool f =
- clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(),
- 0, true, sp_maxRight_Eso);
- f = f &&
- clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(aorta_slices[i], GetBackgroundValue(),
- 0, true, sp_maxRight_Aorta);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Eso, EsophagusForSlice, i, p);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxRight_Aorta, Aorta, i, pp);
- pp[0] -= 2; // Add a margin of 2 mm to include the Aorta 'wall'
- p_AllPoints.push_back(p);
- if (f) {
- p_AllPoints.push_back(pp);
- MaskImagePointType A = p_RightMostPost.back();
- MaskImagePointType B = p;
- MaskImagePointType C = pp;
- double s = (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]);
- if (s>0) p_RightMostAnt.push_back(p);
- else p_RightMostAnt.push_back(pp);
- }
- else { // No more Esophagus in this slice : do nothing
- // p_RightMostAnt.push_back(p);
- p_RightMostPost.pop_back();
- }
-
- // --------------------------------------------------------------------------
- // Find the limit on the Left: most left point between Eso and
- // Eso. (Left is left on screen, coordinate increase)
-
- // Find left limit of Esophagus
- clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(), 0, false, sp_maxLeft_Eso);
- f = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(aorta_slices[i], GetBackgroundValue(), 0, false, sp_maxLeft_Aorta);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Eso, EsophagusForSlice, i, p);
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_maxLeft_Aorta, Aorta, i, pp);
- p_AllPoints.push_back(p);
- pp[0] += 2; // Add a margin of 2 mm to include the 'wall'
- if (f) { // not below Aorta
- p_AllPoints.push_back(pp);
- MaskImagePointType A = p_LeftMostPost.back();
- MaskImagePointType B = p;
- MaskImagePointType C = pp;
- double s = (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]);
- if (s<0) {
- p_LeftMostAnt.push_back(p); // Insert point most at Left, Eso
- }
- else {
- // in this case -> two lines !
- p_LeftMostAnt.push_back(pp); // Insert point most at Left, Aorta (Vert to Aorta)
- // but also consider Aorta to Eso
- p_LeftAorta.push_back(pp);
- p_LeftEso.push_back(p);
- }
- }
- else { // No more Esophagus in this slice : do nothing
- p_LeftMostPost.pop_back();
- //p_LeftMostAnt.push_back(p);
- }
- } // End of slice loop
-
- clitk::WriteListOfLandmarks<MaskImageType>(p_AllPoints, "S8-LR-Eso-Vert.txt");
- clitk::WriteListOfLandmarks<MaskImageType>(p_LeftEso, "S8-Left-Eso.txt");
- clitk::WriteListOfLandmarks<MaskImageType>(p_LeftAorta, "S8-Left-Aorta.txt");
-
- // Now uses these points to limit, slice by slice
- // Line is mainly vertical, so mainDirection=0
- clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
- p_RightMostAnt, p_RightMostPost,
- GetBackgroundValue(), 0, 10);
- clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
- p_LeftMostAnt, p_LeftMostPost,
- GetBackgroundValue(), 0, -10);
- clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
- p_LeftEso,p_LeftAorta,
- GetBackgroundValue(), 0, -10);
- // END
- StopCurrentStep<MaskImageType>(m_Working_Support);
- m_ListOfStations["8"] = m_Working_Support;
- return;
-}
-//--------------------------------------------------------------------
-
-
-//--------------------------------------------------------------------
-template <class ImageType>
-void
-clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_LR_Limits()
+ExtractStation_8_Left_Inf_Limits()
{
-
//--------------------------------------------------------------------
- StartNewStep("[Station8] Left and Right limits arround esophagus with lines from VertebralBody-Aorta-Esophagus");
+ StartNewStep("[Station8] Left limits around esophagus with lines from VertebralBody-Aorta-Esophagus");
// Estract slices for current support for slice by slice processing
std::vector<typename MaskSliceType::Pointer> slices;
clitk::ExtractSlices<MaskImageType>(m_Working_Support, 2, slices);
-
- // Dilate the Esophagus to consider a margins around
- MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt();
- m_Esophagus = clitk::Dilate<MaskImageType>(m_Esophagus, radiusInMM,
- GetBackgroundValue(), GetForegroundValue(), true);
- // m_Esophagus = EnlargeEsophagusDilatationRadiusInferiorly(m_Esophagus);
// Remove what is outside the mediastinum in this enlarged Esophagus -> it allows to select
// 'better' extrema points (not too post).
GetAFDB()->template ReleaseImage<MaskImageType>("Lungs");
// Estract slices of Esophagus (resize like support before to have the same set of slices)
- MaskImagePointer EsophagusForSlice = clitk::ResizeImageLike<MaskImageType>(m_Esophagus, m_Working_Support, GetBackgroundValue());
+ MaskImagePointer EsophagusForSlice =
+ clitk::ResizeImageLike<MaskImageType>(m_Esophagus, m_Working_Support, GetBackgroundValue());
std::vector<typename MaskSliceType::Pointer> eso_slices;
clitk::ExtractSlices<MaskImageType>(EsophagusForSlice, 2, eso_slices);
// Estract slices of Vertebral (resize like support before to have the same set of slices)
MaskImagePointer VertebralBody = GetAFDB()->template GetImage<MaskImageType>("VertebralBody");
VertebralBody = clitk::ResizeImageLike<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
+ // Remove what is outside the support to not consider what is to
+ // posterior in the VertebralBody (post the horizontal line)
+ clitk::And<MaskImageType>(VertebralBody, m_Working_Support, GetBackgroundValue());
+ // writeImage<MaskImageType>(VertebralBody, "vb.mhd");
std::vector<typename MaskSliceType::Pointer> vert_slices;
clitk::ExtractSlices<MaskImageType>(VertebralBody, 2, vert_slices);
// Temporary 3D point
MaskImagePointType p;
+ typename MaskSliceType::PointType minSlicePoint;
+ typename MaskSliceType::PointType maxSlicePoint;
+ clitk::GetMinMaxPointPosition<MaskSliceType>(vert_slices[0], minSlicePoint, maxSlicePoint);
+
// Loop slices
for(uint i=0; i<slices.size() ; i++) {
// Declare all needed 2D points (sp = slice point)
- typename MaskSliceType::PointType sp_MostRightVertebralBody;
+ // typename MaskSliceType::PointType sp_MostRightVertebralBody;
typename MaskSliceType::PointType sp_MostLeftVertebralBody;
typename MaskSliceType::PointType sp_MostLeftAorta;
typename MaskSliceType::PointType sp_temp;
typename MaskSliceType::PointType sp_MostLeftEsophagus;
-
- // Right is at left on screen, coordinate decrease
- // Left is at right on screen, coordinate increase
-
+
/* -------------------------------------------------------------------------------------
- Find most Left point in VertebralBody. Consider only the
- horizontal line which is 'DistanceMaxToAnteriorPartOfTheSpine'
- away from the most ant point.
- */
- typename MaskSliceType::PointType sp_MostAntVertebralBody;
- bool found = false;
- int j=i;
- while (!found) {
- found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[j], GetBackgroundValue(), 1, true, sp_MostAntVertebralBody);
- if (!found) {
- // It should not happen ! But sometimes, a contour is missing or
- // the VertebralBody is not delineated enough inferiorly ... in
- // those cases, we consider the first found slice.
- std::cerr << "No foreground pixels in this VertebralBody slices !?? I try with the previous/next slice" << std::endl;
- j++;
- }
- }
- sp_MostAntVertebralBody[1] += GetDistanceMaxToAnteriorPartOfTheSpine(); // Consider offset
+ Find first point not in mediastinum at LEFT
+ */
+ clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[i], GetBackgroundValue(),
+ 0, false, sp_MostLeftVertebralBody);
+ // clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p);
+ // DD(p);
- // Crop the vertebralbody below this most post line
- vert_slices[j] =
- clitk::CropImageRemoveGreaterThan<MaskSliceType>(vert_slices[j], 1, sp_MostAntVertebralBody[1], false, GetBackgroundValue());
- vert_slices[j] =
- clitk::ResizeImageLike<MaskSliceType>(vert_slices[j], aorta_slices[i], GetBackgroundValue());
- // writeImage<MaskSliceType>(vert_slices[i], "vert-slice-"+toString(i)+".mhd");
+ sp_MostLeftVertebralBody =
+ clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, false,
+ sp_MostLeftVertebralBody, GetBackgroundValue(), 30);
+ // clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p);
+ // DD(p);
- // Find first point not in mediastinum
- clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[j], GetBackgroundValue(), 0, false, sp_MostLeftVertebralBody);
- sp_MostLeftVertebralBody = clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, false, sp_MostLeftVertebralBody, GetBackgroundValue(), 30);
sp_MostLeftVertebralBody[0] += 2; // 2mm margin
- clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[j], GetBackgroundValue(), 0, true, sp_MostRightVertebralBody);
- sp_MostRightVertebralBody = clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, true, sp_MostRightVertebralBody, GetBackgroundValue(),30);
- sp_MostRightVertebralBody[0] -= 2; // 2 mm margin
+ // clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p);
+ // DD(p);
// Convert 2D points into 3D
- clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostRightVertebralBody, VertebralBody, i, p);
- p_MostRightVertebralBody.push_back(p);
clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p);
p_MostLeftVertebralBody.push_back(p);
+ /* -------------------------------------------------------------------------------------
+ Find first point not in mediastinum at RIGHT. Not used yet.
+ clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(vert_slices[i], GetBackgroundValue(),
+ 0, true, sp_MostRightVertebralBody);
+ sp_MostRightVertebralBody =
+ clitk::FindExtremaPointInAGivenLine<MaskSliceType>(mediast_slices[i], 0, true,
+ sp_MostRightVertebralBody, GetBackgroundValue(),30);
+ sp_MostRightVertebralBody[0] -= 2; // 2 mm margin
+
+ // Convert 2D points into 3D
+ clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostRightVertebralBody, VertebralBody, i, p);
+ p_MostRightVertebralBody.push_back(p);
+ */
+
+
/* -------------------------------------------------------------------------------------
Find most Left point in Esophagus
*/
- found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(), 0, false, sp_MostLeftEsophagus);
+ bool found = clitk::FindExtremaPointInAGivenDirection<MaskSliceType>(eso_slices[i], GetBackgroundValue(), 0, false, sp_MostLeftEsophagus);
if (!found) { // No more Esophagus, I remove the previous point
-
- // if (p_MostLeftEsophagus.size() < 1) {
- p_MostLeftVertebralBody.pop_back();
- // }
- // else {
- // // Consider the previous point
- // p = p_MostLeftEsophagus.back();
- // p_MostLeftEsophagus.push_back(p);
- // sp_MostLeftEsophagus = sp_temp; // Retrieve previous 2D position
- // found = true;
- // }
+ //DD("no eso pop back");
+ p_MostLeftVertebralBody.pop_back();
}
else {
clitk::PointsUtils<MaskImageType>::Convert2DTo3D(sp_MostLeftEsophagus, EsophagusForSlice, i, p);
p_MostLeftEsophagus.push_back(p);
- // sp_temp = sp_MostLeftEsophagus; // Store previous 2D position
}
/* -------------------------------------------------------------------------------------
} // End of slice loop
clitk::WriteListOfLandmarks<MaskImageType>(p_MostLeftVertebralBody, "S8-MostLeft-VB-points.txt");
- clitk::WriteListOfLandmarks<MaskImageType>(p_MostRightVertebralBody, "S8-MostRight-VB-points.txt");
+ // clitk::WriteListOfLandmarks<MaskImageType>(p_MostRightVertebralBody, "S8-MostRight-VB-points.txt");
clitk::WriteListOfLandmarks<MaskImageType>(p_MostLeftAorta, "S8-MostLeft-Aorta-points.txt");
clitk::WriteListOfLandmarks<MaskImageType>(p_MostLeftEsophagus, "S8-MostLeft-eso-points.txt");
clitk::SliceBySliceSetBackgroundFromLineSeparation<MaskImageType>(m_Working_Support,
p_MostLeftAorta, p_MostLeftEsophagus,
GetBackgroundValue(), 0, -10);
-
// END
StopCurrentStep<MaskImageType>(m_Working_Support);
m_ListOfStations["8"] = m_Working_Support;
clitk::ExtractLymphStationsFilter<ImageType>::
ExtractStation_8_Remove_Structures()
{
-
//--------------------------------------------------------------------
- StartNewStep("[Station8] remove some structures");
-
Remove_Structures("8", "Aorta");
Remove_Structures("8", "Esophagus");
+ Remove_Structures("8", "VertebralBody");
// END
StopCurrentStep<MaskImageType>(m_Working_Support);
//--------------------------------------------------------------------
-//--------------------------------------------------------------------
-template <class ImageType>
-typename clitk::ExtractLymphStationsFilter<ImageType>::MaskImagePointer
-clitk::ExtractLymphStationsFilter<ImageType>::
-EnlargeEsophagusDilatationRadiusInferiorly(MaskImagePointer & Esophagus)
-{
- // Check if Esophagus is delineated at least until Diaphragm. Now,
- // because we use AutoCrop, Origin[2] gives this max inferior
- // position.
-
- DD("BUGGY DONT USE");
- exit(0);
-
- if (Esophagus->GetOrigin()[2] > m_DiaphragmInferiorLimit) {
- // crop first slice without mask
- MaskImagePointType pt;
- clitk::FindExtremaPointInAGivenDirection<MaskImageType>(Esophagus, GetBackgroundValue(), 2, true, pt);
- DD(pt);
- Esophagus =
- clitk::CropImageRemoveLowerThan<MaskImageType>(Esophagus, 2,
- pt[2],
- false, // AutoCrop
- GetBackgroundValue());
- writeImage<MaskImageType>(Esophagus, "crop-eso.mhd");
-
- std::cout << "Warning Esophagus is not delineated until Diaphragm. I mirror-pad it."
- << std::endl;
- double extraSize = Esophagus->GetOrigin()[2]-m_DiaphragmInferiorLimit;
-
- // Pad with few more slices
- typedef itk::MirrorPadImageFilter<MaskImageType, MaskImageType> PadFilterType;
- typename PadFilterType::Pointer padFilter = PadFilterType::New();
- padFilter->SetInput(Esophagus);
- MaskImageSizeType b;
- b[0] = 0; b[1] = 0; b[2] = (uint)ceil(extraSize/Esophagus->GetSpacing()[2])+1;
- padFilter->SetPadLowerBound(b);
- padFilter->Update();
- Esophagus = padFilter->GetOutput();
- }
- return Esophagus;
-}
-//--------------------------------------------------------------------
-
-
-//--------------------------------------------------------------------
-template <class ImageType>
-void
-clitk::ExtractLymphStationsFilter<ImageType>::
-ExtractStation_8_LR_Limits_old()
-{
- /*
- Station 8: paraeosphageal nodes
-
- Laterally, it is within the pleural envelope and again abuts the
- descending aorta on the left. Reasonably, the delineation of
- Station 8 is limited to the soft tissue surrounding the esophagus
- (Fig. 3C–H).
- */
-
- StartNewStep("[Station8] Right limits (around esophagus)");
- // Get Esophagus
- MaskImagePointer Esophagus = GetAFDB()->template GetImage<MaskImageType>("Esophagus");
-
- // Autocrop to get first slice with starting Esophagus
- Esophagus = clitk::AutoCrop<MaskImageType>(Esophagus, GetBackgroundValue());
-
- // Dilate
- // LR dilatation -> large to keep point inside
- // AP dilatation -> few mm
- // SI dilatation -> enough to cover Diaphragm (old=GOjunctionZ)
- MaskImagePointType radiusInMM = GetEsophagusDiltationForRight();
- Esophagus = EnlargeEsophagusDilatationRadiusInferiorly(Esophagus);
- Esophagus = clitk::Dilate<MaskImageType>(Esophagus,
- radiusInMM,
- GetBackgroundValue(),
- GetForegroundValue(), true);
- writeImage<MaskImageType>(Esophagus, "dilateEso2.mhd");
-
- writeImage<MaskImageType>(m_Working_Support, "before-relpos2.mhd");
-
- // Remove Right (Left on the screen) part according to this
- // dilatated esophagus
- typedef clitk::SliceBySliceRelativePositionFilter<MaskImageType> RelPosFilterType;
- typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
- relPosFilter->VerboseStepFlagOff();
- relPosFilter->WriteStepFlagOff();
- relPosFilter->SetInput(m_Working_Support);
- relPosFilter->SetInputObject(Esophagus);
- relPosFilter->AddOrientationTypeString("NotLeftTo");
- // relPosFilter->InverseOrientationFlagOn(); // Not Left to
- relPosFilter->SetDirection(2); // Z axis
- relPosFilter->UniqueConnectedComponentBySliceOff(); // important
- relPosFilter->SetIntermediateSpacing(4);
- relPosFilter->IntermediateSpacingFlagOn();
- relPosFilter->SetFuzzyThreshold(0.9); // remove few part only
- relPosFilter->RemoveObjectFlagOff();
- relPosFilter->Update();
- m_Working_Support = relPosFilter->GetOutput();
-
- // Get a single 3D CCL
- m_Working_Support = Labelize<MaskImageType>(m_Working_Support, 0, false, 10);
- m_Working_Support = KeepLabels<MaskImageType>(m_Working_Support,
- GetBackgroundValue(),
- GetForegroundValue(), 1, 1, true);
-
-
- /*
- // Re-Add post to Esophagus -> sometimes previous relpos remove some
- // correct part below esophagus.
- MaskImagePointer Esophagus = GetAFDB()->template GetImage<MaskImageType>("Esophagus");
- EnlargeEsophagusDilatationRadiusInferiorly(Esophagus);
- writeImage<MaskImageType>(Esophagus, "e-again.mhd");
- typedef clitk::SliceBySliceRelativePositionFilter<MaskImageType> RelPosFilterType;
- typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New();
- relPosFilter->VerboseStepOff();
- relPosFilter->WriteStepOff();
- relPosFilter->SetInput(m_Working_Support);
- relPosFilter->SetInputObject(Esophagus);
- relPosFilter->SetOrientationTypeString("P");
- relPosFilter->InverseOrientationFlagOff();
- relPosFilter->SetDirection(2); // Z axis
- relPosFilter->UniqueConnectedComponentBySliceOff(); // important
- relPosFilter->SetIntermediateSpacing(4);
- relPosFilter->IntermediateSpacingFlagOn();
- relPosFilter->CombineWithOrFlagOn();
- relPosFilter->SetFuzzyThreshold(0.9); // remove few part only
- relPosFilter->RemoveObjectFlagOff();
- relPosFilter->Update();
- m_Working_Support = relPosFilter->GetOutput();
- */
-
- StopCurrentStep<MaskImageType>(m_Working_Support);
- m_ListOfStations["8"] = m_Working_Support;
-}
-//--------------------------------------------------------------------
-
-
//--------------------------------------------------------------------
template <class TImageType>
void