From: dsarrut Date: Tue, 12 Jul 2011 06:14:58 +0000 (+0200) Subject: Add initial step: select BB supports. X-Git-Tag: v1.3.0~284 X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=commitdiff_plain;h=a27c5cbbf1db44ddc5f519710cddcc1001c15f35;p=clitk.git Add initial step: select BB supports. --- diff --git a/segmentation/clitkExtractLymphStation_7.txx b/segmentation/clitkExtractLymphStation_7.txx index 7bb4643..b12fa52 100644 --- a/segmentation/clitkExtractLymphStation_7.txx +++ b/segmentation/clitkExtractLymphStation_7.txx @@ -11,6 +11,28 @@ ExtractStation_7_SetDefaultValues() SetFuzzyThreshold("7", "RightPulmonaryArtery", 0.3); SetFuzzyThreshold("7", "LeftPulmonaryArtery", 0.5); SetFuzzyThreshold("7", "SVC", 0.2); + SetS7_UseMostInferiorPartOnlyFlag(false); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +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(m_ListOfStations["7"], "seg/Station7.mhd"); + GetAFDB()->SetImageFilename("Station7", "seg/Station7.mhd"); + WriteAFDB(); + } } //-------------------------------------------------------------------- @@ -21,44 +43,212 @@ void clitk::ExtractLymphStationsFilter:: 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 ("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(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(m_Working_Support, 2, + A[2], m_CarinaZ, true, + GetBackgroundValue()); + // Crop trachea + m_Working_Trachea = + clitk::CropImageAlongOneAxis(Trachea, 2, + A[2], m_CarinaZ, true, + GetBackgroundValue()); - // double m_OriginOfRightMiddleLobeBronchusZ = GetAFDB()->GetPoint3D("OriginOfRightMiddleLobeBronchus", 2); - // DD(m_OriginOfRightMiddleLobeBronchusZ); - MaskImagePointer UpperBorderOfLLLBronchus = GetAFDB()->template GetImage("UpperBorderOfLLLBronchus"); + StopCurrentStep(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(UpperBorderOfLLLBronchus, GetBackgroundValue(), 2, true, ps); - double m_UpperBorderOfLLLBronchusZ = ps[2]; +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStation_7_RL_Interior_Limits() +{ + // ---------------------------------------------------------------- + StartNewStep("[Station7] RL limits with bronchi"); + /* - std::vector centroids; - clitk::ComputeCentroids(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 ("RightBronchus"); + m_LeftBronchus = GetAFDB()->template GetImage ("LeftBronchus"); + + // Resize like m_Working_Support + m_LeftBronchus = + clitk::ResizeImageLike(m_LeftBronchus, m_Working_Support, GetBackgroundValue()); + m_RightBronchus = + clitk::ResizeImageLike(m_RightBronchus, m_Working_Support, GetBackgroundValue()); + + // Extract slices, Label, compute centroid, keep most central connected component + std::vector slices_leftbronchus; + std::vector slices_rightbronchus; + std::vector slices_support; + clitk::ExtractSlices(m_LeftBronchus, 2, slices_leftbronchus); + clitk::ExtractSlices(m_RightBronchus, 2, slices_rightbronchus); + clitk::ExtractSlices(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[i], 0, false, 10); + std::vector c; + clitk::ComputeCentroids(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(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[i], 0, false, 10); + std::vector c; + clitk::ComputeCentroids(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 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(slices_rightbronchus[i], most_at_right_index, + most_at_right_index, GetBackgroundValue(), GetForegroundValue()); + } // end c.size + } + + // Joint slices + m_LeftBronchus = clitk::JoinSlices(slices_leftbronchus, m_LeftBronchus, 2); + m_RightBronchus = clitk::JoinSlices(slices_rightbronchus, m_RightBronchus, 2); + + // For Right bronchus, Find most Left point. Remove corner Ant/Right corner + for(uint i=0; i(slices_rightbronchus[i], GetBackgroundValue(), 0, false, p_left); + if (b) { + b = clitk::FindExtremaPointInAGivenDirection(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(slices_support[i], GetBackgroundValue(), region); + } + } + + // For Left bronchus, Find most Right point. Remove corner Ant/Left corner + for(uint i=0; i(slices_leftbronchus[i], GetBackgroundValue(), 0, true, p_right); + if (b) { + b = clitk::FindExtremaPointInAGivenDirection(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 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(slices_support[i], GetBackgroundValue(), region); + } + } + + m_Working_Support = clitk::JoinSlices(slices_support, m_Working_Support, 2); + + // Also remove what is at right of the Right bronchus (left respectively) m_Working_Support = - clitk::CropImageAlongOneAxis(m_Working_Support, 2, - m_UpperBorderOfLLLBronchusZ, - m_CarinaZ, true, - GetBackgroundValue()); - /* Crop trachea */ - m_Working_Trachea = - clitk::CropImageAlongOneAxis(Trachea, 2, - m_UpperBorderOfLLLBronchusZ, - m_CarinaZ, true, - GetBackgroundValue()); + clitk::SliceBySliceRelativePosition(m_Working_Support, m_LeftBronchus, 2, + GetFuzzyThreshold("7", "Bronchi"), "NotLeftTo", + false, 3, false); + m_Working_Support = + clitk::SliceBySliceRelativePosition(m_Working_Support, m_RightBronchus, 2, + GetFuzzyThreshold("7", "Bronchi"), "NotRightTo", + false, 3, false); + + // SECOND PART StopCurrentStep(m_Working_Support); - m_ListOfStations["7"] = m_Working_Support; } //-------------------------------------------------------------------- @@ -67,7 +257,7 @@ ExtractStation_7_SI_Limits() template void clitk::ExtractLymphStationsFilter:: -ExtractStation_7_RL_Limits() +ExtractStation_7_RL_Limits_OLD() { // ---------------------------------------------------------------- StartNewStep("[Station7] Limits with bronchus : RightTo the left bronchus"); @@ -82,6 +272,10 @@ ExtractStation_7_RL_Limits() clitk::ExtractSlices(m_LeftBronchus, 2, slices_leftbronchus); clitk::ExtractSlices(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[i], 0, false, 10); @@ -345,13 +539,22 @@ ExtractStation_7_Remove_Structures() 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(m_Working_Support, GetBackgroundValue(), GetForegroundValue()); + + Remove_Structures("7", "Esophagus"); + // END StopCurrentStep(m_Working_Support); m_ListOfStations["7"] = m_Working_Support; diff --git a/segmentation/clitkExtractLymphStation_8.txx b/segmentation/clitkExtractLymphStation_8.txx index fc39203..7402a62 100644 --- a/segmentation/clitkExtractLymphStation_8.txx +++ b/segmentation/clitkExtractLymphStation_8.txx @@ -8,7 +8,6 @@ void clitk::ExtractLymphStationsFilter:: ExtractStation_8_SetDefaultValues() { - SetDistanceMaxToAnteriorPartOfTheSpine(10); MaskImagePointType p; p[0] = 15; p[1] = 2; p[2] = 1; SetEsophagusDiltationForAnt(p); @@ -19,6 +18,29 @@ ExtractStation_8_SetDefaultValues() } //-------------------------------------------------------------------- +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +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(m_ListOfStations["8"], "seg/Station8.mhd"); + GetAFDB()->SetImageFilename("Station8", "seg/Station8.mhd"); + WriteAFDB(); + } +} +//-------------------------------------------------------------------- + + //-------------------------------------------------------------------- template void @@ -42,63 +64,42 @@ ExtractStation_8_SI_Limits() 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("Carina"); + StartNewStep("[Station8] Sup/Inf limits with LeftLower/RightMiddle Lobe and diaphragm"); - std::vector centroids; - clitk::ComputeCentroids(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(m_Working_Support, + GetBackgroundValue(), A, B, 2, 0, true); + /* ----------------------------------------------- + INFERIOR LIMIT = Diaphragm + ----------------------------------------------- + */ + // Found most inferior part of the lung MaskImagePointer Lungs = GetAFDB()->template GetImage("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("Lungs"); - clitk::PrintMemory(GetVerboseMemoryFlag(), "after release lungs"); - /* Crop support : - Superior limit = carina - Inferior limit = DiaphragmInferiorLimit (old=Gastroesphogeal junction) */ m_Working_Support = clitk::CropImageAlongOneAxis(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 ("VertebralBody"); - MaskImagePointer Aorta = - GetAFDB()->template GetImage ("Aorta"); - - typedef clitk::BooleanOperatorLabelImageFilter 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(m_Working_Support); m_ListOfStations["8"] = m_Working_Support; @@ -110,290 +111,8 @@ ExtractStation_8_SI_Limits() template void clitk::ExtractLymphStationsFilter:: -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 ("VertebralBody"); - - // Consider vertebral body slice by slice - typedef clitk::ExtractSliceFilter ExtractSliceFilterType; - typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New(); - typedef typename ExtractSliceFilterType::SliceType SliceType; - std::vector vertebralSlices; - extractSliceFilter->SetInput(VertebralBody); - extractSliceFilter->SetDirection(2); - extractSliceFilter->Update(); - extractSliceFilter->GetOutputSlices(vertebralSlices); - - // For each slice, compute the most anterior point - std::map vertebralAntPositionBySlice; - for(uint i=0; i(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 vertebralAntPositions; - clitk::PointsUtils::Convert2DMapTo3DList(vertebralAntPositionBySlice, - VertebralBody, - vertebralAntPositions); - - // DEBUG : write list of points - clitk::WriteListOfLandmarks(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 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 it(m_Working_Support, region); - it.GoToBegin(); - while (!it.IsAtEnd()) { - it.Set(GetBackgroundValue()); - ++it; - } - } - } - - StopCurrentStep(m_Working_Support); - m_ListOfStations["8"] = m_Working_Support; -} -//-------------------------------------------------------------------- - - -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -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("Trachea"); - - MaskImagePointer m_Working_Trachea = - clitk::CropImageRemoveGreaterThan(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(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 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 c; - clitk::ComputeCentroids(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(m_Working_Trachea, rightLabel, rightLabel, - GetBackgroundValue(), GetForegroundValue()); - /* - SetBackground(m_Working_Trachea, m_Working_Trachea, - leftLabel, GetBackgroundValue(), false); - */ - LeftBronchus = clitk::Binarize(m_Working_Trachea, leftLabel, leftLabel, - GetBackgroundValue(), GetForegroundValue()); - /* - SetBackground(m_Working_Trachea, m_Working_Trachea, - rightLabel, GetBackgroundValue(), false); - */ - - // Crop images - RightBronchus = clitk::AutoCrop(RightBronchus, GetBackgroundValue()); - LeftBronchus = clitk::AutoCrop(LeftBronchus, GetBackgroundValue()); - - // Insert int AFDB if need after - GetAFDB()->template SetImage ("RightBronchus", "seg/rightBronchus.mhd", - RightBronchus, true); - GetAFDB()->template SetImage ("LeftBronchus", "seg/leftBronchus.mhd", - LeftBronchus, true); - - // Now crop below OriginOfRightMiddleLobeBronchusZ - // It is not done before to keep entire bronchi. - - MaskImagePointer OriginOfRightMiddleLobeBronchus = - GetAFDB()->template GetImage("OriginOfRightMiddleLobeBronchus"); - std::vector centroids; - clitk::ComputeCentroids(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(RightBronchus, 2, - m_OriginOfRightMiddleLobeBronchusZ, - true, // AutoCrop - GetBackgroundValue()); - LeftBronchus = - clitk::CropImageRemoveLowerThan(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(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(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(v, "S8-RightLeftBronchus-points.txt"); - - - // Now uses these points to limit, slice by slice - // line is mainly horizontal, so mainDirection=1 - clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, - m_PostMostInRightBronchus, - m_PostMostInLeftBronchus, - GetBackgroundValue(), 1, 10); - - // Keep main 3D CCL : - m_Working_Support = Labelize(m_Working_Support, 0, false, 10); - m_Working_Support = KeepLabels(m_Working_Support, - GetBackgroundValue(), - GetForegroundValue(), 1, 1, true); - - // Autocrop - m_Working_Support = clitk::AutoCrop(m_Working_Support, GetBackgroundValue()); - - // End of step - StopCurrentStep(m_Working_Support); - // m_ListOfStations["8"] = m_Working_Support; - -} - -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -ExtractStation_8_Ant_Inf_Limits() -{ - //-------------------------------------------------------------------- StartNewStep("[Station8] Ant part: not post to Esophagus"); /* @@ -404,7 +123,6 @@ ExtractStation_8_Ant_Inf_Limits() // Get Esophagus m_Esophagus = GetAFDB()->template GetImage("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 @@ -416,46 +134,17 @@ ExtractStation_8_Ant_Inf_Limits() //prospective, the oesophagus should be excluded from these nodal //stations. - /* NOT YET !! DO IT LATER - - typedef clitk::BooleanOperatorLabelImageFilter 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(m_Esophagus, 2, - m_OriginOfRightMiddleLobeBronchusZ, - true, // AutoCrop - GetBackgroundValue()); + clitk::ResizeImageLike(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(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 RelPosFilterType; typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New(); relPosFilter->VerboseStepFlagOff(); @@ -488,88 +177,10 @@ ExtractStation_8_Ant_Inf_Limits() template void clitk::ExtractLymphStationsFilter:: -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(m_Input, - m_Working_Support, - (short)GetBackgroundValue()); - - // Threshold - typedef itk::BinaryThresholdImageFilter 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(m_Working_Support, injected, GetBackgroundValue()); - - StopCurrentStep(m_Working_Support); - m_ListOfStations["8"] = m_Working_Support; -} -//-------------------------------------------------------------------- - - - -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -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("LeftPulmonaryArtery"); - - // Relative Position : not at Left - typedef clitk::AddRelativePositionConstraintToLabelImageFilter 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("LeftPulmonaryArtery"); - - StopCurrentStep(m_Working_Support); - m_ListOfStations["8"] = m_Working_Support; -} -//-------------------------------------------------------------------- - - -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -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 @@ -682,280 +293,14 @@ ExtractStation_8_Single_CCL_Limits() template void clitk::ExtractLymphStationsFilter:: -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 slices; - clitk::ExtractSlices(m_Working_Support, 2, slices); - - // Dilate the Esophagus to consider a margins around - MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt(); - m_Esophagus = clitk::Dilate(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("Lungs"); - clitk::AndNot(m_Esophagus, Lungs, GetBackgroundValue()); - GetAFDB()->template ReleaseImage("Lungs"); - - // Estract slices of Esophagus (resize like support before to have the same set of slices) - MaskImagePointer EsophagusForSlice = clitk::ResizeImageLike(m_Esophagus, m_Working_Support, GetBackgroundValue()); - - std::vector eso_slices; - clitk::ExtractSlices(EsophagusForSlice, 2, eso_slices); - - // Estract slices of Vertebral (resize like support before to have the same set of slices) - MaskImagePointer VertebralBody = GetAFDB()->template GetImage("VertebralBody"); - VertebralBody = clitk::ResizeImageLike(VertebralBody, m_Working_Support, GetBackgroundValue()); - std::vector vert_slices; - clitk::ExtractSlices(VertebralBody, 2, vert_slices); - - // Estract slices of Aorta (resize like support before to have the same set of slices) - MaskImagePointer Aorta = GetAFDB()->template GetImage("Aorta"); - Aorta = clitk::ResizeImageLike(Aorta, m_Working_Support, GetBackgroundValue()); - std::vector aorta_slices; - clitk::ExtractSlices(Aorta, 2, aorta_slices); - - // Extract slices of Mediastinum (resize like support before to have the same set of slices) - m_Mediastinum = GetAFDB()->template GetImage("Mediastinum"); - m_Mediastinum = clitk::ResizeImageLike(m_Mediastinum, m_Working_Support, GetBackgroundValue()); - std::vector mediast_slices; - clitk::ExtractSlices(m_Mediastinum, 2, mediast_slices); - - // List of points - std::vector p_RightMostAnt; - std::vector p_RightMostPost; - std::vector p_LeftMostAnt; - std::vector p_LeftMostPost; - std::vector p_AllPoints; - std::vector p_LeftAorta; - std::vector 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 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(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(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::Convert2DTo3D(sp_maxRight_Vertebra, VertebralBody, i, p); - p_AllPoints.push_back(p); - vert_slices[i]->TransformIndexToPhysicalPoint(indexL, sp_maxLeft_Vertebra); - clitk::PointsUtils::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::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::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::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::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 c; - clitk::ComputeCentroids(eso_slices[i], GetBackgroundValue(), c); - if (c.size() >1) { - eso_slices[i] = - clitk::CropImageRemoveGreaterThan(eso_slices[i], 1, c[1][1], false, GetBackgroundValue()); - eso_slices[i] = - clitk::ResizeImageLike(eso_slices[i], aorta_slices[i], GetBackgroundValue()); - // writeImage(eso_slices[i], "eso-slice-"+toString(i)+".mhd"); - } - - // Find right limit of Esophagus and Aorta - bool f = - clitk::FindExtremaPointInAGivenDirection(eso_slices[i], GetBackgroundValue(), - 0, true, sp_maxRight_Eso); - f = f && - clitk::FindExtremaPointInAGivenDirection(aorta_slices[i], GetBackgroundValue(), - 0, true, sp_maxRight_Aorta); - clitk::PointsUtils::Convert2DTo3D(sp_maxRight_Eso, EsophagusForSlice, i, p); - clitk::PointsUtils::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(eso_slices[i], GetBackgroundValue(), 0, false, sp_maxLeft_Eso); - f = clitk::FindExtremaPointInAGivenDirection(aorta_slices[i], GetBackgroundValue(), 0, false, sp_maxLeft_Aorta); - clitk::PointsUtils::Convert2DTo3D(sp_maxLeft_Eso, EsophagusForSlice, i, p); - clitk::PointsUtils::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(p_AllPoints, "S8-LR-Eso-Vert.txt"); - clitk::WriteListOfLandmarks(p_LeftEso, "S8-Left-Eso.txt"); - clitk::WriteListOfLandmarks(p_LeftAorta, "S8-Left-Aorta.txt"); - - // Now uses these points to limit, slice by slice - // Line is mainly vertical, so mainDirection=0 - clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, - p_RightMostAnt, p_RightMostPost, - GetBackgroundValue(), 0, 10); - clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, - p_LeftMostAnt, p_LeftMostPost, - GetBackgroundValue(), 0, -10); - clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, - p_LeftEso,p_LeftAorta, - GetBackgroundValue(), 0, -10); - // END - StopCurrentStep(m_Working_Support); - m_ListOfStations["8"] = m_Working_Support; - return; -} -//-------------------------------------------------------------------- - - -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -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 slices; clitk::ExtractSlices(m_Working_Support, 2, slices); - - // Dilate the Esophagus to consider a margins around - MaskImagePointType radiusInMM = GetEsophagusDiltationForAnt(); - m_Esophagus = clitk::Dilate(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). @@ -964,13 +309,18 @@ ExtractStation_8_LR_Limits() GetAFDB()->template ReleaseImage("Lungs"); // Estract slices of Esophagus (resize like support before to have the same set of slices) - MaskImagePointer EsophagusForSlice = clitk::ResizeImageLike(m_Esophagus, m_Working_Support, GetBackgroundValue()); + MaskImagePointer EsophagusForSlice = + clitk::ResizeImageLike(m_Esophagus, m_Working_Support, GetBackgroundValue()); std::vector eso_slices; clitk::ExtractSlices(EsophagusForSlice, 2, eso_slices); // Estract slices of Vertebral (resize like support before to have the same set of slices) MaskImagePointer VertebralBody = GetAFDB()->template GetImage("VertebralBody"); VertebralBody = clitk::ResizeImageLike(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(VertebralBody, m_Working_Support, GetBackgroundValue()); + // writeImage(VertebralBody, "vb.mhd"); std::vector vert_slices; clitk::ExtractSlices(VertebralBody, 2, vert_slices); @@ -1006,80 +356,67 @@ ExtractStation_8_LR_Limits() // Temporary 3D point MaskImagePointType p; + typename MaskSliceType::PointType minSlicePoint; + typename MaskSliceType::PointType maxSlicePoint; + clitk::GetMinMaxPointPosition(vert_slices[0], minSlicePoint, maxSlicePoint); + // Loop slices for(uint i=0; i(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(vert_slices[i], GetBackgroundValue(), + 0, false, sp_MostLeftVertebralBody); + // clitk::PointsUtils::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p); + // DD(p); - // Crop the vertebralbody below this most post line - vert_slices[j] = - clitk::CropImageRemoveGreaterThan(vert_slices[j], 1, sp_MostAntVertebralBody[1], false, GetBackgroundValue()); - vert_slices[j] = - clitk::ResizeImageLike(vert_slices[j], aorta_slices[i], GetBackgroundValue()); - // writeImage(vert_slices[i], "vert-slice-"+toString(i)+".mhd"); + sp_MostLeftVertebralBody = + clitk::FindExtremaPointInAGivenLine(mediast_slices[i], 0, false, + sp_MostLeftVertebralBody, GetBackgroundValue(), 30); + // clitk::PointsUtils::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p); + // DD(p); - // Find first point not in mediastinum - clitk::FindExtremaPointInAGivenDirection(vert_slices[j], GetBackgroundValue(), 0, false, sp_MostLeftVertebralBody); - sp_MostLeftVertebralBody = clitk::FindExtremaPointInAGivenLine(mediast_slices[i], 0, false, sp_MostLeftVertebralBody, GetBackgroundValue(), 30); sp_MostLeftVertebralBody[0] += 2; // 2mm margin - clitk::FindExtremaPointInAGivenDirection(vert_slices[j], GetBackgroundValue(), 0, true, sp_MostRightVertebralBody); - sp_MostRightVertebralBody = clitk::FindExtremaPointInAGivenLine(mediast_slices[i], 0, true, sp_MostRightVertebralBody, GetBackgroundValue(),30); - sp_MostRightVertebralBody[0] -= 2; // 2 mm margin + // clitk::PointsUtils::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p); + // DD(p); // Convert 2D points into 3D - clitk::PointsUtils::Convert2DTo3D(sp_MostRightVertebralBody, VertebralBody, i, p); - p_MostRightVertebralBody.push_back(p); clitk::PointsUtils::Convert2DTo3D(sp_MostLeftVertebralBody, VertebralBody, i, p); p_MostLeftVertebralBody.push_back(p); + /* ------------------------------------------------------------------------------------- + Find first point not in mediastinum at RIGHT. Not used yet. + clitk::FindExtremaPointInAGivenDirection(vert_slices[i], GetBackgroundValue(), + 0, true, sp_MostRightVertebralBody); + sp_MostRightVertebralBody = + clitk::FindExtremaPointInAGivenLine(mediast_slices[i], 0, true, + sp_MostRightVertebralBody, GetBackgroundValue(),30); + sp_MostRightVertebralBody[0] -= 2; // 2 mm margin + + // Convert 2D points into 3D + clitk::PointsUtils::Convert2DTo3D(sp_MostRightVertebralBody, VertebralBody, i, p); + p_MostRightVertebralBody.push_back(p); + */ + + /* ------------------------------------------------------------------------------------- Find most Left point in Esophagus */ - found = clitk::FindExtremaPointInAGivenDirection(eso_slices[i], GetBackgroundValue(), 0, false, sp_MostLeftEsophagus); + bool found = clitk::FindExtremaPointInAGivenDirection(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::Convert2DTo3D(sp_MostLeftEsophagus, EsophagusForSlice, i, p); p_MostLeftEsophagus.push_back(p); - // sp_temp = sp_MostLeftEsophagus; // Store previous 2D position } /* ------------------------------------------------------------------------------------- @@ -1101,7 +438,7 @@ ExtractStation_8_LR_Limits() } // End of slice loop clitk::WriteListOfLandmarks(p_MostLeftVertebralBody, "S8-MostLeft-VB-points.txt"); - clitk::WriteListOfLandmarks(p_MostRightVertebralBody, "S8-MostRight-VB-points.txt"); + // clitk::WriteListOfLandmarks(p_MostRightVertebralBody, "S8-MostRight-VB-points.txt"); clitk::WriteListOfLandmarks(p_MostLeftAorta, "S8-MostLeft-Aorta-points.txt"); clitk::WriteListOfLandmarks(p_MostLeftEsophagus, "S8-MostLeft-eso-points.txt"); @@ -1112,7 +449,6 @@ ExtractStation_8_LR_Limits() clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, p_MostLeftAorta, p_MostLeftEsophagus, GetBackgroundValue(), 0, -10); - // END StopCurrentStep(m_Working_Support); m_ListOfStations["8"] = m_Working_Support; @@ -1127,12 +463,10 @@ void clitk::ExtractLymphStationsFilter:: ExtractStation_8_Remove_Structures() { - //-------------------------------------------------------------------- - StartNewStep("[Station8] remove some structures"); - Remove_Structures("8", "Aorta"); Remove_Structures("8", "Esophagus"); + Remove_Structures("8", "VertebralBody"); // END StopCurrentStep(m_Working_Support); @@ -1142,143 +476,6 @@ ExtractStation_8_Remove_Structures() //-------------------------------------------------------------------- -//-------------------------------------------------------------------- -template -typename clitk::ExtractLymphStationsFilter::MaskImagePointer -clitk::ExtractLymphStationsFilter:: -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(Esophagus, GetBackgroundValue(), 2, true, pt); - DD(pt); - Esophagus = - clitk::CropImageRemoveLowerThan(Esophagus, 2, - pt[2], - false, // AutoCrop - GetBackgroundValue()); - writeImage(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 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 -void -clitk::ExtractLymphStationsFilter:: -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("Esophagus"); - - // Autocrop to get first slice with starting Esophagus - Esophagus = clitk::AutoCrop(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(Esophagus, - radiusInMM, - GetBackgroundValue(), - GetForegroundValue(), true); - writeImage(Esophagus, "dilateEso2.mhd"); - - writeImage(m_Working_Support, "before-relpos2.mhd"); - - // Remove Right (Left on the screen) part according to this - // dilatated esophagus - typedef clitk::SliceBySliceRelativePositionFilter 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(m_Working_Support, 0, false, 10); - m_Working_Support = KeepLabels(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("Esophagus"); - EnlargeEsophagusDilatationRadiusInferiorly(Esophagus); - writeImage(Esophagus, "e-again.mhd"); - typedef clitk::SliceBySliceRelativePositionFilter 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(m_Working_Support); - m_ListOfStations["8"] = m_Working_Support; -} -//-------------------------------------------------------------------- - - //-------------------------------------------------------------------- template void diff --git a/segmentation/clitkExtractLymphStation_Supports.txx b/segmentation/clitkExtractLymphStation_Supports.txx new file mode 100644 index 0000000..903948d --- /dev/null +++ b/segmentation/clitkExtractLymphStation_Supports.txx @@ -0,0 +1,74 @@ + +#include +#include + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStationSupports() +{ + DD("ExtractStationSupports"); + + // Get initial Mediastinum + m_Working_Support = m_Mediastinum = GetAFDB()->template GetImage("Mediastinum", true); + + // Superior limits: CricoidCartilag + // Inferior limits: lung + // (the Mediastinum support already stop at this limit) + + // Consider above Carina + m_CarinaZ = FindCarinaSlicePosition(); + MaskImagePointer m_Support_Superior_to_Carina = + clitk::CropImageRemoveLowerThan(m_Working_Support, 2, + m_CarinaZ, true, GetBackgroundValue()); + MaskImagePointer m_Support_Inferior_to_Carina = + clitk::CropImageRemoveGreaterThan(m_Working_Support, 2, + m_CarinaZ, true, GetBackgroundValue()); + + // Consider only Superior to Carina + m_Working_Support = m_Support_Superior_to_Carina; + + // Step : S1RL + StartNewStep("[Support] sup-inf S1RL"); + /* + Lower border: clavicles bilaterally and, in the midline, the upper + border of the manubrium, 1R designates right-sided nodes, 1L, + left-sided nodes in this region + + 2R: Upper border: apex of the right lung and pleural space, and in + the midline, the upper border of the manubrium + + 2L: Upper border: apex of the left lung and pleural space, and in the + midline, the upper border of the manubrium + */ + + + + + + + + + + // // LeftRight cut along trachea + // MaskImagePointer Trachea = GetAFDB()->GetImage("Trachea"); + // // build a ant-post line for each slice + + // MaskImagePointer m_Support_SupRight = + // clitk::CropImageRemoveLowerThan(m_Working_Support, 2, + // m_CarinaZ, true, GetBackgroundValue()); + + + + + + // Store image filenames into AFDB + m_ListOfSupports["S1R"] = m_Working_Support; + writeImage(m_ListOfSupports["S1R"], "seg/Support_S1R.mhd"); + GetAFDB()->SetImageFilename("Support_S1R", "seg/Support_S1R.mhd"); + WriteAFDB(); +} +//-------------------------------------------------------------------- + + diff --git a/segmentation/clitkExtractLymphStations.ggo b/segmentation/clitkExtractLymphStations.ggo index d789824..96ac994 100644 --- a/segmentation/clitkExtractLymphStations.ggo +++ b/segmentation/clitkExtractLymphStations.ggo @@ -20,28 +20,27 @@ option "output" o "Output lungs mask filename" string no option "station" - "Force to compute station even if already exist in the DB" string no multiple section "Options for Station 8" -option "maxAntSpine" - "Distance max to anterior part of the spine in mm" double no default="10" -option "esophagusDilatationForAnt" - "Dilatation of esophagus, in mm, for 'anterior' limits (default=15,2,1)" double no multiple -option "esophagusDilatationForRight" - "Dilatation of esophagus, in mm, for 'right' limits (default=5,10,1)" double no multiple -option "tS8_Esophagus" - "Threshold for 'Post' to dilated Esophagus" double default="0.5" no -option "injectedThresholdForS8" - "Threshold injected areas in the ct" double default="150" no +option "S8_esophagusDilatationForAnt" - "Dilatation of esophagus, in mm, for 'anterior' limits (default=15,2,1)" double no multiple +option "S8_esophagusDilatationForRight" - "Dilatation of esophagus, in mm, for 'right' limits (default=5,10,1)" double no multiple +option "S8_ft_Esophagus" - "Fuzzy Threshold for 'Post' to dilated Esophagus" double default="0.5" no section "Options for Station 7" -option "tS7_Bronchi" - "Threshold for Left/Right bronchi" double default="0.1" no -option "tS7_LeftSuperiorPulmonaryVein" - "Threshold for LeftSuperiorPulmonaryVein" double default="0.3" no -option "tS7_RightSuperiorPulmonaryVein" - "Threshold for RightSuperiorPulmonaryVein" double default="0.2" no -option "tS7_RightPulmonaryArtery" - "Threshold for RightPulmonaryArtery" double default="0.3" no -option "tS7_LeftPulmonaryArtery" - "Threshold for LeftPulmonaryArtery (NOT USEFUL YET)" double default="0.5" no -option "tS7_SVC" - "Threshold for SVC" double default="0.2" no +option "S7_ft_Bronchi" - "Fuzzy Threshold for Left/Right bronchi" double default="0.1" no +option "S7_ft_LeftSuperiorPulmonaryVein" - "Fuzzy Threshold for LeftSuperiorPulmonaryVein" double default="0.3" no +option "S7_ft_RightSuperiorPulmonaryVein" - "Fuzzy Threshold for RightSuperiorPulmonaryVein" double default="0.2" no +option "S7_ft_RightPulmonaryArtery" - "Fuzzy Threshold for RightPulmonaryArtery" double default="0.3" no +option "S7_ft_LeftPulmonaryArtery" - "Fuzzy Threshold for LeftPulmonaryArtery (NOT USEFUL YET)" double default="0.5" no +option "S7_ft_SVC" - "Fuzzy Threshold for SVC" double default="0.2" no +option "S7_UseMostInferiorPartOnly" - "Inferior separation with S8." flag off section "Options for Station 3A" -option "tS3A_Sternum" - "Threshold for Post to Sternum" double default="0.5" no -option "tS3A_SubclavianArtery" - "Threshold for Ant to SubclavianArtery" double default="0.2" no +option "S3A_ft_Sternum" - "Fuzzy Threshold for Post to Sternum" double default="0.5" no +option "S3A_ft_SubclavianArtery" - "Fuzzy Threshold for Ant to SubclavianArtery" double default="0.2" no section "Options for Station 2RL" -option "tS2RL_CommonCarotidArtery" - "Threshold for NotAntTo to CommonCarotidArtery" double default="0.7" no -option "tS2RL_BrachioCephalicTrunk" - "Threshold for NotAntTo to BrachioCephalicTrunk" double default="0.7" no -option "tS2RL_BrachioCephalicVein" - "Threshold for NotAntTo to BrachioCephalicVein" double default="0.3" no -option "tS2RL_Aorta" - "Threshold for RightTo to Aorta" double default="0.7" no -option "tS2RL_SubclavianArteryRight" - "Threshold for NotLeft to SubclavianArteryRight" double default="0.5" no -option "tS2RL_SubclavianArteryLeft" - "Threshold for NotRight to SubclavianArteryLeft" double default="0.8" no +option "S2RL_ft_CommonCarotidArtery" - "Threshold for NotAntTo to CommonCarotidArtery" double default="0.7" no +option "S2RL_ft_BrachioCephalicTrunk" - "Threshold for NotAntTo to BrachioCephalicTrunk" double default="0.7" no +option "S2RL_ft_BrachioCephalicVein" - "Threshold for NotAntTo to BrachioCephalicVein" double default="0.3" no +option "S2RL_ft_Aorta" - "Threshold for RightTo to Aorta" double default="0.7" no +option "S2RL_ft_SubclavianArteryRight" - "Threshold for NotLeft to SubclavianArteryRight" double default="0.5" no +option "S2RL_ft_SubclavianArteryLeft" - "Threshold for NotRight to SubclavianArteryLeft" double default="0.8" no diff --git a/segmentation/clitkExtractLymphStationsFilter.h b/segmentation/clitkExtractLymphStationsFilter.h index 8d160b1..dd99c5f 100644 --- a/segmentation/clitkExtractLymphStationsFilter.h +++ b/segmentation/clitkExtractLymphStationsFilter.h @@ -76,6 +76,9 @@ namespace clitk { typedef itk::Image MaskSliceType; typedef typename MaskSliceType::Pointer MaskSlicePointer; typedef typename MaskSliceType::PointType MaskSlicePointType; + typedef typename MaskSliceType::RegionType MaskSliceRegionType; + typedef typename MaskSliceType::SizeType MaskSliceSizeType; + typedef typename MaskSliceType::IndexType MaskSliceIndexType; /** ImageDimension constants */ itkStaticConstMacro(ImageDimension, unsigned int, ImageType::ImageDimension); @@ -87,8 +90,8 @@ namespace clitk { itkSetMacro(ForegroundValue, MaskImagePixelType); // Station 8 - itkSetMacro(DistanceMaxToAnteriorPartOfTheSpine, double); - itkGetConstMacro(DistanceMaxToAnteriorPartOfTheSpine, double); + // itkSetMacro(DistanceMaxToAnteriorPartOfTheSpine, double); + //itkGetConstMacro(DistanceMaxToAnteriorPartOfTheSpine, double); itkSetMacro(EsophagusDiltationForAnt, MaskImagePointType); itkGetConstMacro(EsophagusDiltationForAnt, MaskImagePointType); itkSetMacro(EsophagusDiltationForRight, MaskImagePointType); @@ -97,6 +100,9 @@ namespace clitk { itkGetConstMacro(InjectedThresholdForS8, double); // Station 7 + itkGetConstMacro(S7_UseMostInferiorPartOnlyFlag, bool); + itkSetMacro(S7_UseMostInferiorPartOnlyFlag, bool); + itkBooleanMacro(S7_UseMostInferiorPartOnlyFlag); // All stations bool GetComputeStation(std::string s); @@ -116,6 +122,7 @@ namespace clitk { MaskImagePointer m_Mediastinum; MaskImagePointer m_Working_Support; std::map m_ListOfStations; + std::map m_ListOfSupports; MaskImagePixelType m_BackgroundValue; MaskImagePixelType m_ForegroundValue; std::map m_ComputeStationMap; @@ -123,12 +130,20 @@ namespace clitk { bool CheckForStation(std::string station); void Remove_Structures(std::string station, std::string s); + // Functions common to several stations + void FindLineForS7S8Separation(MaskImagePointType & A, MaskImagePointType & B); + double FindCarinaSlicePosition(); + void FindLeftAndRightBronchi(); + // Global parameters typedef std::map FuzzyThresholdByStructureType; std::map m_FuzzyThreshold; + // Station's supports + void ExtractStationSupports(); + // Station 8 - double m_DistanceMaxToAnteriorPartOfTheSpine; + // double m_DistanceMaxToAnteriorPartOfTheSpine; double m_DiaphragmInferiorLimit; double m_CarinaZ; double m_OriginOfRightMiddleLobeBronchusZ; @@ -136,22 +151,16 @@ namespace clitk { MaskImagePointer m_Esophagus; MaskImagePointType m_EsophagusDiltationForAnt; MaskImagePointType m_EsophagusDiltationForRight; - MaskImagePointer EnlargeEsophagusDilatationRadiusInferiorly(MaskImagePointer & eso); + void ExtractStation_8(); void ExtractStation_8_SetDefaultValues(); void ExtractStation_8_SI_Limits(); - void ExtractStation_8_Post_Limits(); - void ExtractStation_8_Ant_Sup_Limits(); - void ExtractStation_8_Ant_Inf_Limits(); - void ExtractStation_8_Ant_Injected_Limits(); - void ExtractStation_8_LR_1_Limits(); - void ExtractStation_8_LR_2_Limits(); + void ExtractStation_8_Ant_Limits(); + void ExtractStation_8_Left_Sup_Limits(); + void ExtractStation_8_Left_Inf_Limits(); void ExtractStation_8_Single_CCL_Limits(); - void ExtractStation_8_LR_Limits(); void ExtractStation_8_Remove_Structures(); - void ExtractStation_8_LR_Limits_old(); - void ExtractStation_8_LR_Limits_old2(); - + // Station 3P void ExtractStation_3P(); void ExtractStation_3P_SetDefaultValues(); @@ -186,9 +195,13 @@ namespace clitk { void ExtractStation_7(); void ExtractStation_7_SetDefaultValues(); void ExtractStation_7_SI_Limits(); - void ExtractStation_7_RL_Limits(); + void ExtractStation_7_RL_Interior_Limits(); + + + void ExtractStation_7_RL_Limits_OLD(); void ExtractStation_7_Posterior_Limits(); void ExtractStation_7_Remove_Structures(); + bool m_S7_UseMostInferiorPartOnlyFlag; MaskImagePointer m_Working_Trachea; MaskImagePointer m_LeftBronchus; MaskImagePointer m_RightBronchus; @@ -226,6 +239,7 @@ namespace clitk { #ifndef ITK_MANUAL_INSTANTIATION #include "clitkExtractLymphStationsFilter.txx" +#include "clitkExtractLymphStation_Supports.txx" #include "clitkExtractLymphStation_8.txx" #include "clitkExtractLymphStation_3P.txx" #include "clitkExtractLymphStation_2RL.txx" diff --git a/segmentation/clitkExtractLymphStationsFilter.txx b/segmentation/clitkExtractLymphStationsFilter.txx index 1823e99..58f2991 100644 --- a/segmentation/clitkExtractLymphStationsFilter.txx +++ b/segmentation/clitkExtractLymphStationsFilter.txx @@ -72,6 +72,12 @@ GenerateOutputInformation() { m_Input = dynamic_cast(itk::ProcessObject::GetInput(0)); m_Mediastinum = GetAFDB()->template GetImage ("Mediastinum"); + // Global supports for stations + StartNewStep("Supports for stations"); + StartSubStep(); + ExtractStationSupports(); + StopSubStep(); + // Extract Station8 StartNewStep("Station 8"); StartSubStep(); @@ -85,10 +91,12 @@ GenerateOutputInformation() { StopSubStep(); // Extract Station2RL - StartNewStep("Station 2RL"); - StartSubStep(); - ExtractStation_2RL(); - StopSubStep(); + /* + StartNewStep("Station 2RL"); + StartSubStep(); + ExtractStation_2RL(); + StopSubStep(); + */ // Extract Station3A StartNewStep("Station 3A"); @@ -189,32 +197,6 @@ AddComputeStation(std::string station) //-------------------------------------------------------------------- -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -ExtractStation_8() -{ - if (CheckForStation("8")) { - ExtractStation_8_SI_Limits(); - ExtractStation_8_Post_Limits(); - ExtractStation_8_Ant_Sup_Limits(); - ExtractStation_8_Ant_Inf_Limits(); - ExtractStation_8_LR_1_Limits(); - ExtractStation_8_LR_2_Limits(); - ExtractStation_8_LR_Limits(); - ExtractStation_8_Ant_Injected_Limits(); - ExtractStation_8_Single_CCL_Limits(); - ExtractStation_8_Remove_Structures(); - // Store image filenames into AFDB - writeImage(m_ListOfStations["8"], "seg/Station8.mhd"); - GetAFDB()->SetImageFilename("Station8", "seg/Station8.mhd"); - WriteAFDB(); - } -} -//-------------------------------------------------------------------- - - //-------------------------------------------------------------------- template void @@ -239,26 +221,6 @@ ExtractStation_3P() //-------------------------------------------------------------------- -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -ExtractStation_7() { - if (CheckForStation("7")) { - ExtractStation_7_SI_Limits(); - ExtractStation_7_RL_Limits(); - ExtractStation_7_Posterior_Limits(); - // ExtractStation_8_Single_CCL_Limits(); // Yes the same than 8 - ExtractStation_7_Remove_Structures(); - // Store image filenames into AFDB - writeImage(m_ListOfStations["7"], "seg/Station7.mhd"); - GetAFDB()->SetImageFilename("Station7", "seg/Station7.mhd"); - WriteAFDB(); - } -} -//-------------------------------------------------------------------- - - //-------------------------------------------------------------------- template void @@ -371,6 +333,171 @@ GetFuzzyThreshold(std::string station, std::string tag) //-------------------------------------------------------------------- +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +FindLineForS7S8Separation(MaskImagePointType & A, MaskImagePointType & B) +{ + // Create line from A to B with + // A = upper border of LLL at left + // B = lower border of bronchus intermedius (BI) or RightMiddleLobeBronchus + + try { + GetAFDB()->GetPoint3D("LineForS7S8Separation_Begin", A); + GetAFDB()->GetPoint3D("LineForS7S8Separation_End", B); + } + catch(clitk::ExceptionObject & o) { + + DD("FindLineForS7S8Separation"); + // Load LeftLowerLobeBronchus and get centroid point + MaskImagePointer LeftLowerLobeBronchus = + GetAFDB()->template GetImage ("LeftLowerLobeBronchus"); + std::vector c; + clitk::ComputeCentroids(LeftLowerLobeBronchus, GetBackgroundValue(), c); + A = c[1]; + + // Load RightMiddleLobeBronchus and get superior point (not centroid here) + MaskImagePointer RightMiddleLobeBronchus = + GetAFDB()->template GetImage ("RightMiddleLobeBronchus"); + bool b = FindExtremaPointInAGivenDirection(RightMiddleLobeBronchus, + GetBackgroundValue(), + 2, false, B); + if (!b) { + clitkExceptionMacro("Error while searching most superior point in RightMiddleLobeBronchus. Abort"); + } + + // Insert into the DB + GetAFDB()->SetPoint3D("LineForS7S8Separation_Begin", A); + GetAFDB()->SetPoint3D("LineForS7S8Separation_End", B); + } +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +double +clitk::ExtractLymphStationsFilter:: +FindCarinaSlicePosition() +{ + double z; + try { + z = GetAFDB()->GetDouble("CarinaZ"); + } + catch(clitk::ExceptionObject e) { + DD("FindCarinaSlicePosition"); + // Get Carina + MaskImagePointer Carina = GetAFDB()->template GetImage("Carina"); + + // Get Centroid and Z value + std::vector centroids; + clitk::ComputeCentroids(Carina, GetBackgroundValue(), centroids); + // We dont need Carina structure from now + Carina->Delete(); + + // Put inside the AFDB + GetAFDB()->SetPoint3D("CarinaPoint", centroids[1]); + GetAFDB()->SetDouble("CarinaZ", centroids[1][2]); + WriteAFDB(); + z = centroids[1][2]; + } + return z; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +FindLeftAndRightBronchi() +{ + try { + m_RightBronchus = GetAFDB()->template GetImage ("RightBronchus"); + m_LeftBronchus = GetAFDB()->template GetImage ("LeftBronchus"); + } + catch(clitk::ExceptionObject & o) { + + DD("FindLeftAndRightBronchi"); + // The goal is to separate the trachea inferiorly to the carina into + // a Left and Right bronchus. + + // Get the trachea + MaskImagePointer Trachea = GetAFDB()->template GetImage("Trachea"); + + // Get the Carina position + m_CarinaZ = FindCarinaSlicePosition(); + + // Consider only inferiorly to the Carina + MaskImagePointer m_Working_Trachea = + clitk::CropImageRemoveGreaterThan(Trachea, 2, m_CarinaZ, true, // AutoCrop + GetBackgroundValue()); + + // Labelize the trachea + m_Working_Trachea = Labelize(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 + MaskImagePointer RightBronchus; + MaskImagePointer LeftBronchus; + typedef itk::ImageSliceIteratorWithIndex 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 from 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 c; + clitk::ComputeCentroids(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(m_Working_Trachea, rightLabel, rightLabel, + GetBackgroundValue(), GetForegroundValue()); + /* + SetBackground(m_Working_Trachea, m_Working_Trachea, + leftLabel, GetBackgroundValue(), false); + */ + LeftBronchus = clitk::Binarize(m_Working_Trachea, leftLabel, leftLabel, + GetBackgroundValue(), GetForegroundValue()); + /* + SetBackground(m_Working_Trachea, m_Working_Trachea, + rightLabel, GetBackgroundValue(), false); + */ + + // Crop images + RightBronchus = clitk::AutoCrop(RightBronchus, GetBackgroundValue()); + LeftBronchus = clitk::AutoCrop(LeftBronchus, GetBackgroundValue()); + + // Insert int AFDB if need after + GetAFDB()->template SetImage ("RightBronchus", "seg/rightBronchus.mhd", + RightBronchus, true); + GetAFDB()->template SetImage ("LeftBronchus", "seg/leftBronchus.mhd", + LeftBronchus, true); + } +} +//-------------------------------------------------------------------- #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX diff --git a/segmentation/clitkExtractLymphStationsGenericFilter.txx b/segmentation/clitkExtractLymphStationsGenericFilter.txx index ffa6c3c..8b6167e 100644 --- a/segmentation/clitkExtractLymphStationsGenericFilter.txx +++ b/segmentation/clitkExtractLymphStationsGenericFilter.txx @@ -70,9 +70,9 @@ SetOptionsFromArgsInfoToFilter(FilterType * f) f->SetAFDBFilename(mArgsInfo.afdb_arg); // Station 8 - f->SetDistanceMaxToAnteriorPartOfTheSpine(mArgsInfo.maxAntSpine_arg); - f->SetFuzzyThreshold("8", "Esophagus", mArgsInfo.tS8_Esophagus_arg); - f->SetInjectedThresholdForS8(mArgsInfo.injectedThresholdForS8_arg); + //f->SetDistanceMaxToAnteriorPartOfTheSpine(mArgsInfo.S8_maxAntSpine_arg); + f->SetFuzzyThreshold("8", "Esophagus", mArgsInfo.S8_ft_Esophagus_arg); + // f->SetInjectedThresholdForS8(mArgsInfo.tS8_injectedThreshold_arg); // Check multiple options for radius dilatation /* @@ -83,27 +83,27 @@ SetOptionsFromArgsInfoToFilter(FilterType * f) */ typename FilterType::MaskImagePointType p; p[0] = 7; p[1] = 5; p[2] = 0; // default value - if (mArgsInfo.esophagusDilatationForAnt_given == 3) { + if (mArgsInfo.S8_esophagusDilatationForAnt_given == 3) { for(uint i=0; i<3; i++) - p[i] = mArgsInfo.esophagusDilatationForAnt_arg[i]; + p[i] = mArgsInfo.S8_esophagusDilatationForAnt_arg[i]; } else { - if (mArgsInfo.esophagusDilatationForAnt_given == 1) { + if (mArgsInfo.S8_esophagusDilatationForAnt_given == 1) { for(uint i=0; i<3; i++) - p[i] = mArgsInfo.esophagusDilatationForAnt_arg[0]; + p[i] = mArgsInfo.S8_esophagusDilatationForAnt_arg[0]; } } f->SetEsophagusDiltationForAnt(p); p[0] = 5; p[1] = 10; p[2] = 1; // default value - if (mArgsInfo.esophagusDilatationForRight_given == 3) { + if (mArgsInfo.S8_esophagusDilatationForRight_given == 3) { for(uint i=0; i<3; i++) - p[i] = mArgsInfo.esophagusDilatationForRight_arg[i]; + p[i] = mArgsInfo.S8_esophagusDilatationForRight_arg[i]; } else { - if (mArgsInfo.esophagusDilatationForRight_given == 1) { + if (mArgsInfo.S8_esophagusDilatationForRight_given == 1) { for(uint i=0; i<3; i++) - p[i] = mArgsInfo.esophagusDilatationForRight_arg[0]; + p[i] = mArgsInfo.S8_esophagusDilatationForRight_arg[0]; } } f->SetEsophagusDiltationForRight(p); @@ -112,24 +112,25 @@ SetOptionsFromArgsInfoToFilter(FilterType * f) f->AddComputeStation(mArgsInfo.station_arg[i]); // Station 7 - f->SetFuzzyThreshold("7", "Bronchi", mArgsInfo.tS7_Bronchi_arg); - f->SetFuzzyThreshold("7", "LeftSuperiorPulmonaryVein", mArgsInfo.tS7_LeftSuperiorPulmonaryVein_arg); - f->SetFuzzyThreshold("7", "RightSuperiorPulmonaryVein", mArgsInfo.tS7_RightSuperiorPulmonaryVein_arg); - f->SetFuzzyThreshold("7", "RightPulmonaryArtery", mArgsInfo.tS7_RightPulmonaryArtery_arg); - f->SetFuzzyThreshold("7", "LeftPulmonaryArtery", mArgsInfo.tS7_LeftPulmonaryArtery_arg); - f->SetFuzzyThreshold("7", "SVC", mArgsInfo.tS7_SVC_arg); + f->SetFuzzyThreshold("7", "Bronchi", mArgsInfo.S7_ft_Bronchi_arg); + f->SetFuzzyThreshold("7", "LeftSuperiorPulmonaryVein", mArgsInfo.S7_ft_LeftSuperiorPulmonaryVein_arg); + f->SetFuzzyThreshold("7", "RightSuperiorPulmonaryVein", mArgsInfo.S7_ft_RightSuperiorPulmonaryVein_arg); + f->SetFuzzyThreshold("7", "RightPulmonaryArtery", mArgsInfo.S7_ft_RightPulmonaryArtery_arg); + f->SetFuzzyThreshold("7", "LeftPulmonaryArtery", mArgsInfo.S7_ft_LeftPulmonaryArtery_arg); + f->SetFuzzyThreshold("7", "SVC", mArgsInfo.S7_ft_SVC_arg); + f->SetS7_UseMostInferiorPartOnlyFlag(mArgsInfo.S7_UseMostInferiorPartOnly_flag); // Station 3A - f->SetFuzzyThreshold("3A", "Sternum", mArgsInfo.tS3A_Sternum_arg); - f->SetFuzzyThreshold("3A", "SubclavianArtery", mArgsInfo.tS3A_SubclavianArtery_arg); + f->SetFuzzyThreshold("3A", "Sternum", mArgsInfo.S3A_ft_Sternum_arg); + f->SetFuzzyThreshold("3A", "SubclavianArtery", mArgsInfo.S3A_ft_SubclavianArtery_arg); // Station 2RL - f->SetFuzzyThreshold("2RL", "CommonCarotidArtery", mArgsInfo.tS2RL_CommonCarotidArtery_arg); - f->SetFuzzyThreshold("2RL", "BrachioCephalicTrunk", mArgsInfo.tS2RL_BrachioCephalicTrunk_arg); - f->SetFuzzyThreshold("2RL", "BrachioCephalicVein", mArgsInfo.tS2RL_BrachioCephalicVein_arg); - f->SetFuzzyThreshold("2RL", "Aorta", mArgsInfo.tS2RL_Aorta_arg); - f->SetFuzzyThreshold("2RL", "SubclavianArteryLeft", mArgsInfo.tS2RL_SubclavianArteryLeft_arg); - f->SetFuzzyThreshold("2RL", "SubclavianArteryRight", mArgsInfo.tS2RL_SubclavianArteryRight_arg); + f->SetFuzzyThreshold("2RL", "CommonCarotidArtery", mArgsInfo.S2RL_ft_CommonCarotidArtery_arg); + f->SetFuzzyThreshold("2RL", "BrachioCephalicTrunk", mArgsInfo.S2RL_ft_BrachioCephalicTrunk_arg); + f->SetFuzzyThreshold("2RL", "BrachioCephalicVein", mArgsInfo.S2RL_ft_BrachioCephalicVein_arg); + f->SetFuzzyThreshold("2RL", "Aorta", mArgsInfo.S2RL_ft_Aorta_arg); + f->SetFuzzyThreshold("2RL", "SubclavianArteryLeft", mArgsInfo.S2RL_ft_SubclavianArteryLeft_arg); + f->SetFuzzyThreshold("2RL", "SubclavianArteryRight", mArgsInfo.S2RL_ft_SubclavianArteryRight_arg); } //--------------------------------------------------------------------