X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=segmentation%2FclitkExtractLymphStation_3P.txx;h=d1be71047093fb38dce38b1c25d571ff878a6230;hb=a339bdc482ea9752ec53195bc9a47e8b05dba582;hp=effcd4fd6f51a88a6d407bd81faf0895c50b47c7;hpb=9e5e119f00eb9051aa56e480aba3e86d053cdb80;p=clitk.git diff --git a/segmentation/clitkExtractLymphStation_3P.txx b/segmentation/clitkExtractLymphStation_3P.txx index effcd4f..d1be710 100644 --- a/segmentation/clitkExtractLymphStation_3P.txx +++ b/segmentation/clitkExtractLymphStation_3P.txx @@ -11,6 +11,7 @@ ExtractStation_3P_SetDefaultValues() } //-------------------------------------------------------------------- + //-------------------------------------------------------------------- template void @@ -19,37 +20,452 @@ ExtractStation_3P_SI_Limits() { /* Apex of the chest & Carina. - */ + */ StartNewStep("[Station 3P] Inf/Sup limits with apex of the chest and carina"); - writeImage(m_Working_Support, "support.mhd"); - // Get Carina position (has been determined in Station8) m_CarinaZ = GetAFDB()->GetDouble("CarinaZ"); - DD(m_CarinaZ); - // Get Apex of the Chest + // Get Apex of the Chest. The "lungs" structure is autocroped so we + // consider the most superior point. MaskImagePointer Lungs = GetAFDB()->template GetImage("Lungs"); - DD("lung ok"); + MaskImageIndexType index = Lungs->GetLargestPossibleRegion().GetIndex(); + index += Lungs->GetLargestPossibleRegion().GetSize(); MaskImagePointType p; - bool found = clitk::FindExtremaPointInAGivenDirection(Lungs, - GetBackgroundValue(), - 1, true, p); - DD(found); - DD(p); + Lungs->TransformIndexToPhysicalPoint(index, p); + p[2] -= 5; // 5 mm below because the autocrop is slightly above the apex double m_ApexOfTheChest = p[2]; - DD(m_ApexOfTheChest); /* Crop support : Superior limit = carina Inferior limit = Apex of the chest */ m_Working_Support = clitk::CropImageAlongOneAxis(m_Working_Support, 2, - m_ApexOfTheChest, - m_CarinaZ, true, + m_CarinaZ, + m_ApexOfTheChest, true, GetBackgroundValue()); StopCurrentStep(m_Working_Support); m_ListOfStations["3P"] = m_Working_Support; } //-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStation_3P_Remove_Structures() +{ + /* + 3 - (sup) remove Aorta, VB, subcl + not LR subcl ! -> a séparer LR ? + (inf) remove Eso, Aorta, Azygousvein + */ + + StartNewStep("[Station 3P] Remove some structures."); + + MaskImagePointer Aorta = GetAFDB()->template GetImage("Aorta"); + clitk::AndNot(m_Working_Support, Aorta, GetBackgroundValue()); + + MaskImagePointer VertebralBody = GetAFDB()->template GetImage("VertebralBody"); + clitk::AndNot(m_Working_Support, VertebralBody); + + MaskImagePointer SubclavianArtery = GetAFDB()->template GetImage("SubclavianArtery"); + clitk::AndNot(m_Working_Support, SubclavianArtery); + + MaskImagePointer Esophagus = GetAFDB()->template GetImage("Esophagus"); + clitk::AndNot(m_Working_Support, Esophagus); + + MaskImagePointer AzygousVein = GetAFDB()->template GetImage("AzygousVein"); + clitk::AndNot(m_Working_Support, AzygousVein); + + MaskImagePointer Thyroid = GetAFDB()->template GetImage("Thyroid"); + clitk::AndNot(m_Working_Support, Thyroid); + + StopCurrentStep(m_Working_Support); + m_ListOfStations["3P"] = m_Working_Support; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStation_3P_Ant_Limits() +{ + /* + Ant Post limit : + + Anteriorly, it is in contact with the posterior aspect of Stations + 1–2 superiorly (Fig. 2A–C) and with Stations 4R and 4L inferiorly + (Fig. 2D–I and 3A–C). The anterior limit of Station 3P is kept + posterior to the trachea, which is defined by an imaginary + horizontal line running along the posterior wall of the trachea + (Fig. 2B,E, red line). Posteriorly, it is delineated along the + anterior and lateral borders of the vertebral body until an + imaginary horizontal line running 1 cm posteriorly to the + anterior border of the vertebral body (Fig. 2D). + + 1 - post to the trachea : define an imaginary line just post the + Trachea and remove what is anterior. + + */ + StartNewStep("[Station 3P] Ant limits with Trachea "); + + // Load Trachea + MaskImagePointer Trachea = GetAFDB()->template GetImage("Trachea"); + + // Crop like current support (need by SliceBySliceSetBackgroundFromLineSeparation after) + Trachea = + clitk::ResizeImageLike(Trachea, m_Working_Support, GetBackgroundValue()); + + // Slice by slice, determine the most post point of the trachea (A) + // and consider a second point on the same horizontal line (B) + std::vector p_A; + std::vector p_B; + std::vector slices; + clitk::ExtractSlices(Trachea, 2, slices); + MaskImagePointType p; + typename MaskSliceType::PointType sp; + for(uint i=0; i(slices[i], 0, true, 100); + slices[i] = KeepLabels(slices[i], GetBackgroundValue(), + GetForegroundValue(), 1, 1, true); + // Find most posterior point + clitk::FindExtremaPointInAGivenDirection(slices[i], GetBackgroundValue(), + 1, false, sp); + clitk::PointsUtils::Convert2DTo3D(sp, Trachea, i, p); + p_A.push_back(p); + p[0] -= 20; + p_B.push_back(p); + } + clitk::WriteListOfLandmarks(p_A, "trachea-post.txt"); + + // Remove Ant part above those lines + clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, + p_A, p_B, + GetBackgroundValue(), + 1, 10); + // End, release memory + GetAFDB()->template ReleaseImage("Trachea"); + StopCurrentStep(m_Working_Support); + m_ListOfStations["3P"] = m_Working_Support; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStation_3P_Post_Limits() +{ + /* + Ant Post limit : + + Anteriorly, it is in contact with the posterior aspect of Stations + 1–2 superiorly (Fig. 2A–C) and with Stations 4R and 4L inferiorly + (Fig. 2D–I and 3A–C). The anterior limit of Station 3P is kept + posterior to the trachea, which is defined by an imaginary + horizontal line running along the posterior wall of the trachea + (Fig. 2B,E, red line). Posteriorly, it is delineated along the + anterior and lateral borders of the vertebral body until an + imaginary horizontal line running 1 cm posteriorly to the + anterior border of the vertebral body (Fig. 2D). + + 2 - post to the trachea : define an imaginary line just post the + Trachea and remove what is anterior. + + */ + StartNewStep("[Station 3P] Post limits with VertebralBody "); + + // Load VertebralBody + MaskImagePointer VertebralBody = GetAFDB()->template GetImage("VertebralBody"); + + // Crop like current support (need by SliceBySliceSetBackgroundFromLineSeparation after) + VertebralBody = clitk::ResizeImageLike(VertebralBody, m_Working_Support, GetBackgroundValue()); + + writeImage(VertebralBody, "vb.mhd"); + + // Compute VertebralBody most Ant position (again because slices + // changes). Slice by slice, determine the most post point of the + // trachea (A) and consider a second point on the same horizontal + // line (B) + std::vector p_A; + std::vector p_B; + std::vector slices; + clitk::ExtractSlices(VertebralBody, 2, slices); + MaskImagePointType p; + typename MaskSliceType::PointType sp; + for(uint i=0; i(slices[i], GetBackgroundValue(), + 1, true, sp); + + // If the VertebralBody stop superiorly before the end of + // m_Working_Support, we consider the same previous point. + if (!found) { + p = p_A.back(); + p[2] += VertebralBody->GetSpacing()[2]; + p_A.push_back(p); + p = p_B.back(); + p[2] += VertebralBody->GetSpacing()[2]; + p_B.push_back(p); + } + else { + clitk::PointsUtils::Convert2DTo3D(sp, VertebralBody, i, p); + p[1] += 10; // Consider 10 mm more post + p_A.push_back(p); + p[0] -= 20; + p_B.push_back(p); + } + } + clitk::WriteListOfLandmarks(p_A, "vb-ant.txt"); + + + // Remove Ant part above those lines + clitk::SliceBySliceSetBackgroundFromLineSeparation(m_Working_Support, + p_A, p_B, + GetBackgroundValue(), + 1, -10); + writeImage(m_Working_Support, "a.mhd"); + + StopCurrentStep(m_Working_Support); + m_ListOfStations["3P"] = m_Working_Support; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStation_3P_LR_sup_Limits() +{ + /* + "On the right side, the limit is defined by the air–soft-tissue + interface. On the left side, it is defined by the air–tissue + interface superiorly (Fig. 2A–C) and the aorta inferiorly + (Figs. 2D–I and 3A–C)." + + sup : + Resizelike support : Trachea, SubclavianArtery + Trachea, slice by slice, get centroid trachea + SubclavianArtery, slice by slice, CCL + prendre la 1ère à L et R, not at Left + + */ + StartNewStep("[Station 3P] Left/Right limits (superior part) "); + + // Load structures + MaskImagePointer Trachea = GetAFDB()->template GetImage("Trachea"); + MaskImagePointer SubclavianArtery = GetAFDB()->template GetImage("SubclavianArtery"); + + // Crop like current support + Trachea = clitk::ResizeImageLike(Trachea, m_Working_Support, GetBackgroundValue()); + SubclavianArtery = clitk::ResizeImageLike(SubclavianArtery, m_Working_Support, GetBackgroundValue()); + + writeImage(Trachea, "tr.mhd"); + writeImage(SubclavianArtery, "sca.mhd"); + + // Get list of slices + std::vector slices_support; + std::vector slices_trachea; + std::vector slices_subclavianartery; + clitk::ExtractSlices(m_Working_Support, 2, slices_support); + clitk::ExtractSlices(Trachea, 2, slices_trachea); + clitk::ExtractSlices(SubclavianArtery, 2, slices_subclavianartery); + + // Loop on slices + std::vector points; + MaskImagePointType p; + for(uint i=0; i centroids; + typename MaskSliceType::PointType c; + ComputeCentroids(slices_trachea[i], GetBackgroundValue(), centroids); + c = centroids[1]; + + // [debug] Store point + clitk::PointsUtils::Convert2DTo3D(centroids[1], Trachea, i, p); + points.push_back(p); + + // Get Right and Left CCL in SubclavianArtery + slices_subclavianartery[i] = Labelize(slices_subclavianartery[i], 0, true, 10); + ComputeCentroids(slices_subclavianartery[i], GetBackgroundValue(), centroids); + + if (centroids.size() > 1) { + // Determine the one at Right/Left -> first after Trachea + // centroid + typename MaskSliceType::PointType right; + typename MaskSliceType::PointType left; + int label_right=-1; + int label_left=-1; + right[0] = c[0]-100; + left[0] = c[0]+100; + for(uint j=1; j= right[0]) { + right = centroids[j]; + label_right = j; + } + } + if (centroids[j][0] > c[0]) { // At Left of Trachea centroid + if (centroids[j][0] <= left[0]) { + left = centroids[j]; + label_left = j; + } + } + } + + if (label_right != -1) { + + // Debug points + clitk::PointsUtils::Convert2DTo3D(centroids[label_right], SubclavianArtery, i, p); + points.push_back(p); + + // Set Background and ForegroundValue according to label_right + MaskSlicePointer object = + clitk::Binarize(slices_subclavianartery[i], label_right, label_right, + GetBackgroundValue(), GetForegroundValue()); + + // Relative Position : not at Right + typedef clitk::AddRelativePositionConstraintToLabelImageFilter RelPosFilterType; + typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New(); + relPosFilter->VerboseStepFlagOff(); + relPosFilter->WriteStepFlagOff(); + relPosFilter->SetBackgroundValue(GetBackgroundValue()); + relPosFilter->SetInput(slices_support[i]); + relPosFilter->SetInputObject(object); + relPosFilter->AddOrientationTypeString("R"); + relPosFilter->SetInverseOrientationFlag(true); + // relPosFilter->SetIntermediateSpacing(3); + relPosFilter->SetIntermediateSpacingFlag(false); + relPosFilter->SetFuzzyThreshold(0.7); + relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop + relPosFilter->Update(); + slices_support[i] = relPosFilter->GetOutput(); + + // Relative Position : not Anterior + relPosFilter = RelPosFilterType::New(); + relPosFilter->VerboseStepFlagOff(); + relPosFilter->WriteStepFlagOff(); + relPosFilter->SetBackgroundValue(GetBackgroundValue()); + relPosFilter->SetInput(slices_support[i]); + relPosFilter->SetInputObject(object); + relPosFilter->AddOrientationTypeString("A"); + relPosFilter->SetInverseOrientationFlag(true); + // relPosFilter->SetIntermediateSpacing(3); + relPosFilter->SetIntermediateSpacingFlag(false); + relPosFilter->SetFuzzyThreshold(0.7); + relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop + relPosFilter->Update(); + slices_support[i] = relPosFilter->GetOutput(); + + } // End RelativePosition for Right + + + if (label_left != -1) { + + // Debug points + clitk::PointsUtils::Convert2DTo3D(centroids[label_left], SubclavianArtery, i, p); + points.push_back(p); + + // Set Background and ForegroundValue according to label_right + MaskSlicePointer object = + clitk::Binarize(slices_subclavianartery[i], label_left, label_left, + GetBackgroundValue(), GetForegroundValue()); + + // Relative Position : not at Right + typedef clitk::AddRelativePositionConstraintToLabelImageFilter RelPosFilterType; + typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New(); + relPosFilter->VerboseStepFlagOff(); + relPosFilter->WriteStepFlagOff(); + relPosFilter->SetBackgroundValue(GetBackgroundValue()); + relPosFilter->SetInput(slices_support[i]); + relPosFilter->SetInputObject(object); + relPosFilter->AddOrientationTypeString("L"); + relPosFilter->SetInverseOrientationFlag(true); + // relPosFilter->SetIntermediateSpacing(3); + relPosFilter->SetIntermediateSpacingFlag(false); + relPosFilter->SetFuzzyThreshold(0.7); + relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop + relPosFilter->Update(); + slices_support[i] = relPosFilter->GetOutput(); + + // Relative Position : not Anterior + relPosFilter = RelPosFilterType::New(); + relPosFilter->VerboseStepFlagOff(); + relPosFilter->WriteStepFlagOff(); + relPosFilter->SetBackgroundValue(GetBackgroundValue()); + relPosFilter->SetInput(slices_support[i]); + relPosFilter->SetInputObject(object); + relPosFilter->AddOrientationTypeString("A"); + relPosFilter->SetInverseOrientationFlag(true); + // relPosFilter->SetIntermediateSpacing(3); + relPosFilter->SetIntermediateSpacingFlag(false); + relPosFilter->SetFuzzyThreshold(0.7); + relPosFilter->AutoCropFlagOff(); // important ! because we join the slices after this loop + relPosFilter->Update(); + slices_support[i] = relPosFilter->GetOutput(); + + } + + + } // if centroids.size > 1 + } // End loop slices + + // Joint slices + m_Working_Support = clitk::JoinSlices(slices_support, m_Working_Support, 2); + + // Save list points + clitk::WriteListOfLandmarks(points, "subcl-lr.txt"); + + + StopCurrentStep(m_Working_Support); + m_ListOfStations["3P"] = m_Working_Support; +} +//-------------------------------------------------------------------- + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ExtractStation_3P_LR_inf_Limits() +{ + /* + "On the right side, the limit is defined by the air–soft-tissue + interface. On the left side, it is defined by the air–tissue + interface superiorly (Fig. 2A–C) and the aorta inferiorly + (Figs. 2D–I and 3A–C)." + + inf : not Right to Azygousvein + */ + StartNewStep("[Station 3P] Left/Right limits (inferior part) "); + + // Load structures + MaskImagePointer AzygousVein = GetAFDB()->template GetImage("AzygousVein"); + + typedef clitk::AddRelativePositionConstraintToLabelImageFilter RelPosFilterType; + typename RelPosFilterType::Pointer relPosFilter = RelPosFilterType::New(); + relPosFilter->VerboseStepFlagOff(); + relPosFilter->WriteStepFlagOff(); + relPosFilter->SetBackgroundValue(GetBackgroundValue()); + relPosFilter->SetInput(m_Working_Support); + relPosFilter->SetInputObject(AzygousVein); + relPosFilter->AddOrientationTypeString("R"); + relPosFilter->SetInverseOrientationFlag(true); + // relPosFilter->SetIntermediateSpacing(3); + relPosFilter->SetIntermediateSpacingFlag(false); + relPosFilter->SetFuzzyThreshold(0.7); + relPosFilter->AutoCropFlagOn(); + relPosFilter->Update(); + m_Working_Support = relPosFilter->GetOutput(); + + StopCurrentStep(m_Working_Support); + m_ListOfStations["3P"] = m_Working_Support; +} +//--------------------------------------------------------------------