#include #include //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: ExtractStationSupports() { // Get initial Mediastinum m_Working_Support = m_Mediastinum = this->GetAFDB()->template GetImage("Mediastinum", true); // Consider sup/inf to Carina double m_CarinaZ = FindCarina(); 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()); m_ListOfSupports["Support_Superior_to_Carina"] = m_Support_Superior_to_Carina; m_ListOfSupports["Support_Inferior_to_Carina"] = m_Support_Inferior_to_Carina; writeImage(m_Support_Inferior_to_Carina, "seg/Support_Inf_Carina.mhd"); this->GetAFDB()->SetImageFilename("Support_Inf_Carina", "seg/Support_Inf_Carina.mhd"); writeImage(m_Support_Superior_to_Carina, "seg/Support_Sup_Carina.mhd"); this->GetAFDB()->SetImageFilename("Support_Sup_Carina", "seg/Support_Sup_Carina.mhd"); // S1RL Support_SupInf_S1RL(); Support_LeftRight_S1R_S1L(); // S2RL Support_SupInf_S2R_S2L(); Support_LeftRight_S2R_S2L(); // S4RL Support_SupInf_S4R_S4L(); Support_LeftRight_S4R_S4L(); // Post limits of S1,S2,S4 Support_Post_S1S2S4(); // S3AP Support_S3P(); Support_S3A(); // S5, S6 Support_S5(); Support_S6(); // Below Carina S7,8,9,10 m_ListOfSupports["S7"] = clitk::Clone(m_Support_Inferior_to_Carina); m_ListOfSupports["S8"] = clitk::Clone(m_Support_Inferior_to_Carina); m_ListOfSupports["S9"] = clitk::Clone(m_Support_Inferior_to_Carina); m_ListOfSupports["S10"] = clitk::Clone(m_Support_Inferior_to_Carina); m_ListOfSupports["S11"] = clitk::Clone(m_Support_Inferior_to_Carina); // Store image filenames into AFDB writeImage(m_ListOfSupports["S1R"], "seg/Support_S1R.mhd"); this->GetAFDB()->SetImageFilename("Support_S1R", "seg/Support_S1R.mhd"); writeImage(m_ListOfSupports["S1L"], "seg/Support_S1L.mhd"); this->GetAFDB()->SetImageFilename("Support_S1L", "seg/Support_S1L.mhd"); writeImage(m_ListOfSupports["S2L"], "seg/Support_S2L.mhd"); this->GetAFDB()->SetImageFilename("Support_S2L", "seg/Support_S2L.mhd"); writeImage(m_ListOfSupports["S2R"], "seg/Support_S2R.mhd"); this->GetAFDB()->SetImageFilename("Support_S2R", "seg/Support_S2R.mhd"); writeImage(m_ListOfSupports["S3P"], "seg/Support_S3P.mhd"); this->GetAFDB()->SetImageFilename("Support_S3P", "seg/Support_S3P.mhd"); writeImage(m_ListOfSupports["S3A"], "seg/Support_S3A.mhd"); this->GetAFDB()->SetImageFilename("Support_S3A", "seg/Support_S3A.mhd"); writeImage(m_ListOfSupports["S4L"], "seg/Support_S4L.mhd"); this->GetAFDB()->SetImageFilename("Support_S4L", "seg/Support_S4L.mhd"); writeImage(m_ListOfSupports["S4R"], "seg/Support_S4R.mhd"); this->GetAFDB()->SetImageFilename("Support_S4R", "seg/Support_S4R.mhd"); writeImage(m_ListOfSupports["S5"], "seg/Support_S5.mhd"); this->GetAFDB()->SetImageFilename("Support_S5", "seg/Support_S5.mhd"); writeImage(m_ListOfSupports["S6"], "seg/Support_S6.mhd"); this->GetAFDB()->SetImageFilename("Support_S6", "seg/Support_S6.mhd"); writeImage(m_ListOfSupports["S7"], "seg/Support_S7.mhd"); this->GetAFDB()->SetImageFilename("Support_S7", "seg/Support_S7.mhd"); writeImage(m_ListOfSupports["S8"], "seg/Support_S8.mhd"); this->GetAFDB()->SetImageFilename("Support_S8", "seg/Support_S8.mhd"); writeImage(m_ListOfSupports["S9"], "seg/Support_S9.mhd"); this->GetAFDB()->SetImageFilename("Support_S9", "seg/Support_S9.mhd"); writeImage(m_ListOfSupports["S10"], "seg/Support_S10.mhd"); this->GetAFDB()->SetImageFilename("Support_S10", "seg/Support_S10.mhd"); writeImage(m_ListOfSupports["S11"], "seg/Support_S11.mhd"); this->GetAFDB()->SetImageFilename("Support_S11", "seg/Support_S11.mhd"); WriteAFDB(); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_SupInf_S1RL() { // Step : S1RL StartNewStep("[Support] Sup-Inf S1RL"); /* 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 => apex / manubrium = up Sternum */ m_Working_Support = m_ListOfSupports["Support_Superior_to_Carina"]; MaskImagePointer Sternum = this->GetAFDB()->template GetImage ("Sternum"); MaskImagePointType p; p[0] = p[1] = p[2] = 0.0; // to avoid warning clitk::FindExtremaPointInAGivenDirection(Sternum, GetBackgroundValue(), 2, false, p); MaskImagePointer S1RL = clitk::CropImageRemoveLowerThan(m_Working_Support, 2, p[2], true, GetBackgroundValue()); m_Working_Support = clitk::CropImageRemoveGreaterThan(m_Working_Support, 2, p[2], true, GetBackgroundValue()); m_ListOfSupports["S1RL"] = S1RL; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_LeftRight_S1R_S1L() { // Step S1RL : Left-Right StartNewStep("[Support] Left-Right S1R S1L"); std::vector A; std::vector B; // Search for centroid positions of trachea MaskImagePointer Trachea = this->GetAFDB()->template GetImage ("Trachea"); MaskImagePointer S1RL = m_ListOfSupports["S1RL"]; Trachea = clitk::ResizeImageLike(Trachea, S1RL, GetBackgroundValue()); std::vector slices; clitk::ExtractSlices(Trachea, 2, slices); for(uint i=0; i(slices[i], 0, false, 10); std::vector c; clitk::ComputeCentroids(slices[i], GetBackgroundValue(), c); ImagePointType a,b; clitk::PointsUtils::Convert2DTo3D(c[1], Trachea, i, a); A.push_back(a); b = a; b[1] += 50; B.push_back(b); } clitk::WriteListOfLandmarks(A, "S1LR_A.txt"); clitk::WriteListOfLandmarks(B, "S1LR_B.txt"); // Clone support MaskImagePointer S1R = clitk::Clone(S1RL); MaskImagePointer S1L = clitk::Clone(S1RL); // Right part clitk::SliceBySliceSetBackgroundFromLineSeparation(S1R, A, B, GetBackgroundValue(), 0, -10); S1R = clitk::AutoCrop(S1R, GetBackgroundValue()); m_ListOfSupports["S1R"] = S1R; // Left part clitk::SliceBySliceSetBackgroundFromLineSeparation(S1L, A, B, GetBackgroundValue(), 0, 10); S1L = clitk::AutoCrop(S1L, GetBackgroundValue()); m_ListOfSupports["S1L"] = S1L; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_SupInf_S2R_S2L() { // Step : S2RL Sup-Inf limits /* 2R Lower border: intersection of caudal margin of innominate vein with the trachea 2L Lower border: superior border of the aortic arch */ StartNewStep("[Support] Sup-Inf S2RL"); m_Working_Support = m_ListOfSupports["Support_Superior_to_Carina"]; // S2R Caudal Margin Of Left BrachiocephalicVein MaskImagePointer BrachioCephalicVein = this->GetAFDB()->template GetImage("BrachioCephalicVein"); MaskImagePointType p; clitk::FindExtremaPointInAGivenDirection(BrachioCephalicVein, GetBackgroundValue(), 2, true, p); // I add slightly more than a slice --> NO !! double CaudalMarginOfLeftBrachiocephalicVeinZ=p[2];//+ 1.1*m_Working_Support->GetSpacing()[2]; this->GetAFDB()->SetDouble("CaudalMarginOfLeftBrachiocephalicVeinZ", CaudalMarginOfLeftBrachiocephalicVeinZ); MaskImagePointer S2R = clitk::CropImageRemoveLowerThan(m_Working_Support, 2, CaudalMarginOfLeftBrachiocephalicVeinZ, true, GetBackgroundValue()); // S2L : Top Of Aortic Arch MaskImagePointer Aorta = this->GetAFDB()->template GetImage("Aorta"); clitk::FindExtremaPointInAGivenDirection(Aorta, GetBackgroundValue(), 2, false, p); // Save the TopOfAorticArchZ this->GetAFDB()->SetDouble("TopOfAorticArchZ", p[2]); // I substract slightly more than a slice to respect delineation double TopOfAorticArchZ=p[2]- 1.1*m_Working_Support->GetSpacing()[2]; // this->GetAFDB()->SetDouble("TopOfAorticArchZ", TopOfAorticArchZ); MaskImagePointer S2L = clitk::CropImageRemoveLowerThan(m_Working_Support, 2, TopOfAorticArchZ, true, GetBackgroundValue()); /* // S2RL: Superior support, I use inferior part of S1RL MaskImagePointer S1L = m_ListOfSupports["S1L"]; clitk::FindExtremaPointInAGivenDirection(S1L, GetBackgroundValue(), 2, true, p); DD(p); S2L = clitk::CropImageRemoveGreaterThan(S2L, 2, p[2], true, GetBackgroundValue()); MaskImagePointer S1R = m_ListOfSupports["S1R"]; clitk::FindExtremaPointInAGivenDirection(S1R, GetBackgroundValue(), 2, true, p); DD(p); S2R = clitk::CropImageRemoveGreaterThan(S2R, 2, p[2], true, GetBackgroundValue()); */ // Superior limits, use Sternum (but not strictly inf to S1RL MaskImagePointer Sternum = this->GetAFDB()->template GetImage ("Sternum"); clitk::FindExtremaPointInAGivenDirection(Sternum, GetBackgroundValue(), 2, false, p); // Add one slice p[2] = p[2] + m_Working_Support->GetSpacing()[2]; S2L = clitk::CropImageRemoveGreaterThan(S2L, 2, p[2], true, GetBackgroundValue()); S2R = clitk::CropImageRemoveGreaterThan(S2R, 2, p[2], true, GetBackgroundValue()); // The is the end m_ListOfSupports["S2L"] = S2L; m_ListOfSupports["S2R"] = S2R; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_LeftRight_S2R_S2L() { // --------------------------------------------------------------------------- /* Step : S2RL LeftRight As for lymph node station 4R, 2R includes nodes extending to the left lateral border of the trachea Rod says: "For station 2 there is a shift, dividing 2R from 2L, from midline to the left paratracheal border." */ StartNewStep("[Support] Separate 2R/2L according to Trachea"); MaskImagePointer S2R = m_ListOfSupports["S2R"]; MaskImagePointer S2L = m_ListOfSupports["S2L"]; S2R = LimitsWithTrachea(S2R, 0, 1, -10); S2L = LimitsWithTrachea(S2L, 0, 1, 10); m_ListOfSupports["S2R"] = S2R; m_ListOfSupports["S2L"] = S2L; this->GetAFDB()->template ReleaseImage("Trachea"); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_SupInf_S4R_S4L() { // --------------------------------------------------------------------------- /* Step : S4RL Sup-Inf - start at the end of 2R and 2L - stop ? - 4R Rod says : "The inferior border is at the lower border of the azygous vein." Rod says : difficulties (was : "ends at the upper lobe bronchus or where the right pulmonary artery crosses the midline of the mediastinum ") - 4L Rod says : "The lower border is to upper margin of the left main pulmonary artery." (was LLL bronchus) */ StartNewStep("[Support] Sup-Inf limits of 4R/4L"); // Start from the support MaskImagePointer S4RL = clitk::Clone(m_Working_Support); MaskImagePointer S4R = clitk::Clone(S4RL); MaskImagePointer S4L = clitk::Clone(S4RL); // Keep only what is lower than S2 MaskImagePointer S2R = m_ListOfSupports["S2R"]; MaskImagePointer S2L = m_ListOfSupports["S2L"]; MaskImagePointType p; // Right part clitk::FindExtremaPointInAGivenDirection(S2R, GetBackgroundValue(), 2, true, p); S4R = clitk::CropImageRemoveGreaterThan(S4R, 2, p[2], true, GetBackgroundValue()); // Left part clitk::FindExtremaPointInAGivenDirection(S2L, GetBackgroundValue(), 2, true, p); S4L = clitk::CropImageRemoveGreaterThan(S4L, 2, p[2], true, GetBackgroundValue()); // Get AzygousVein and limit according to LowerBorderAzygousVein MaskImagePointer LowerBorderAzygousVein = this->GetAFDB()->template GetImage("LowerBorderAzygousVein"); std::vector c; clitk::ComputeCentroids(LowerBorderAzygousVein, GetBackgroundValue(), c); S4R = clitk::CropImageRemoveLowerThan(S4R, 2, c[1][2], true, GetBackgroundValue()); S4R = clitk::AutoCrop(S4R, GetBackgroundValue()); m_ListOfSupports["S4R"] = S4R; // Limit according to LeftPulmonaryArtery MaskImagePointer LeftPulmonaryArtery = this->GetAFDB()->template GetImage("LeftPulmonaryArtery"); clitk::FindExtremaPointInAGivenDirection(LeftPulmonaryArtery, GetBackgroundValue(), 2, false, p); S4L = clitk::CropImageRemoveLowerThan(S4L, 2, p[2], true, GetBackgroundValue()); S4L = clitk::AutoCrop(S4L, GetBackgroundValue()); m_ListOfSupports["S4L"] = S4L; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_LeftRight_S4R_S4L() { // --------------------------------------------------------------------------- /* Step : S4RL LeftRight - 4R: includes right paratracheal nodes, and pretracheal nodes extending to the left lateral border of trachea - 4L: includes nodes to the left of the left lateral border of the trachea, medial to the ligamentum arteriosum => same than 2RL */ StartNewStep("[Support] Left Right separation of 4R/4L"); MaskImagePointer S4R = m_ListOfSupports["S4R"]; MaskImagePointer S4L = m_ListOfSupports["S4L"]; S4R = LimitsWithTrachea(S4R, 0, 1, -10); S4L = LimitsWithTrachea(S4L, 0, 1, 10); m_ListOfSupports["S4R"] = S4R; m_ListOfSupports["S4L"] = S4L; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template typename clitk::ExtractLymphStationsFilter::MaskImagePointer clitk::ExtractLymphStationsFilter:: LimitsWithTrachea(MaskImageType * input, int extremaDirection, int lineDirection, double offset) { MaskImagePointType min, max; GetMinMaxBoundary(input, min, max); return LimitsWithTrachea(input, extremaDirection, lineDirection, offset, max[2]); } template typename clitk::ExtractLymphStationsFilter::MaskImagePointer clitk::ExtractLymphStationsFilter:: LimitsWithTrachea(MaskImageType * input, int extremaDirection, int lineDirection, double offset, double maxSupPosition) { /* Take the input mask, consider the trachea and limit according to Left border of the trachea. Keep at Left or at Right according to the offset */ // Read the trachea MaskImagePointer Trachea = this->GetAFDB()->template GetImage("Trachea"); // Find extrema post positions std::vector tracheaLeftPositionsA; std::vector tracheaLeftPositionsB; // Crop Trachea only on the Sup-Inf axes, without autocrop // Trachea = clitk::ResizeImageLike(Trachea, input, GetBackgroundValue()); MaskImagePointType min, max; GetMinMaxBoundary(input, min, max); Trachea = clitk::CropImageAlongOneAxis(Trachea, 2, min[2], max[2], false, GetBackgroundValue()); // Select the main CCL (because of bronchus) Trachea = SliceBySliceKeepMainCCL(Trachea, GetBackgroundValue(), GetForegroundValue()); // Slice by slice, build the separation line clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition(Trachea, GetBackgroundValue(), 2, extremaDirection, false, // Left lineDirection, // Vertical line -1, // margins tracheaLeftPositionsA, tracheaLeftPositionsB); // Do not consider trachea above the limit int indexMax=tracheaLeftPositionsA.size(); for(uint i=0; i maxSupPosition) { indexMax = i; i = tracheaLeftPositionsA.size(); // stop loop } } tracheaLeftPositionsA.erase(tracheaLeftPositionsA.begin()+indexMax, tracheaLeftPositionsA.end()); tracheaLeftPositionsB.erase(tracheaLeftPositionsB.begin()+indexMax, tracheaLeftPositionsB.end()); // Cut post to this line clitk::SliceBySliceSetBackgroundFromLineSeparation(input, tracheaLeftPositionsA, tracheaLeftPositionsB, GetBackgroundValue(), extremaDirection, offset); MaskImagePointer output = clitk::AutoCrop(input, GetBackgroundValue()); return output; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_Post_S1S2S4() { StartNewStep("[Support] Post limits of S1RL, S2RL, S4RL"); double m_ApexOfTheChest = FindApexOfTheChest(); // Post limits with Trachea MaskImagePointer S1R = m_ListOfSupports["S1R"]; MaskImagePointer S1L = m_ListOfSupports["S1L"]; MaskImagePointer S2R = m_ListOfSupports["S2R"]; MaskImagePointer S2L = m_ListOfSupports["S2L"]; MaskImagePointer S4R = m_ListOfSupports["S4R"]; MaskImagePointer S4L = m_ListOfSupports["S4L"]; S1L = LimitsWithTrachea(S1L, 1, 0, -10, m_ApexOfTheChest); S1R = LimitsWithTrachea(S1R, 1, 0, -10, m_ApexOfTheChest); S2R = LimitsWithTrachea(S2R, 1, 0, -10, m_ApexOfTheChest); S2L = LimitsWithTrachea(S2L, 1, 0, -10, m_ApexOfTheChest); S4R = LimitsWithTrachea(S4R, 1, 0, -10, m_ApexOfTheChest); S4L = LimitsWithTrachea(S4L, 1, 0, -10, m_ApexOfTheChest); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_S3P() { StartNewStep("[Support] Ant limits of S3P and Post limits of S1RL, S2RL, S4RL"); // Initial S3P support MaskImagePointer S3P = clitk::Clone(m_ListOfSupports["Support_Superior_to_Carina"]); // Stop at Lung Apex double m_ApexOfTheChest = FindApexOfTheChest(); S3P = clitk::CropImageRemoveGreaterThan(S3P, 2, m_ApexOfTheChest, true, GetBackgroundValue()); // Ant limits with Trachea S3P = LimitsWithTrachea(S3P, 1, 0, 10); m_ListOfSupports["S3P"] = S3P; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_S3A() { StartNewStep("[Support] Sup-Inf and Post limits for S3A"); // Initial S3A support MaskImagePointer S3A = clitk::Clone(m_ListOfSupports["Support_Superior_to_Carina"]); // Stop at Lung Apex or like S2/S1 (upper border Sternum - manubrium) ? //double m_ApexOfTheChest = FindApexOfTheChest(); MaskImagePointer Sternum = this->GetAFDB()->template GetImage ("Sternum"); MaskImagePointType p; p[0] = p[1] = p[2] = 0.0; // to avoid warning clitk::FindExtremaPointInAGivenDirection(Sternum, GetBackgroundValue(), 2, false, p); p[2] += Sternum->GetSpacing()[2]; // we add one slice to stop 3A at the same slice than Sternum stop S3A = clitk::CropImageRemoveGreaterThan(S3A, 2, //m_ApexOfTheChest p[2], true, GetBackgroundValue()); // Ant limits with Trachea S3A = LimitsWithTrachea(S3A, 1, 0, -10); m_ListOfSupports["S3A"] = S3A; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_S5() { StartNewStep("[Support] Sup-Inf limits S5 with aorta"); // Initial S5 support MaskImagePointer S5 = clitk::Clone(this->GetAFDB()->template GetImage("Mediastinum", true)); // Sup limits with Aorta double sup = FindInferiorBorderOfAorticArch(); // Inf limits with "upper rim of the left main pulmonary artery" // For the moment only, it will change. MaskImagePointer PulmonaryTrunk = this->GetAFDB()->template GetImage("PulmonaryTrunk"); MaskImagePointType p; p[0] = p[1] = p[2] = 0.0; // to avoid warning clitk::FindExtremaPointInAGivenDirection(PulmonaryTrunk, GetBackgroundValue(), 2, false, p); p[2] += PulmonaryTrunk->GetSpacing()[2]; // Cut Sup/Inf S5 = clitk::CropImageAlongOneAxis(S5, 2, p[2], sup, true, GetBackgroundValue()); m_ListOfSupports["S5"] = S5; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractLymphStationsFilter:: Support_S6() { StartNewStep("[Support] Sup-Inf limits S6 with aorta"); // Initial S6 support like S3A MaskImagePointer S6 = clitk::Clone(m_ListOfSupports["S3A"]); // Inf Sup limits with Aorta double sup = FindSuperiorBorderOfAorticArch(); double inf = FindInferiorBorderOfAorticArch(); // Cut Sup/Inf S6 = clitk::CropImageAlongOneAxis(S6, 2, inf, sup, true, GetBackgroundValue()); m_ListOfSupports["S6"] = S6; } //--------------------------------------------------------------------