X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=segmentation%2FclitkExtractLymphStation_Supports.txx;h=f480d842b2990cc3c16156b2500b5add84281ed7;hb=5a7da4aedae5c204bc55c187717193e5950f9a44;hp=1008ff48dfa3b53f16dac112280ca3ae2458a92f;hpb=e1098dccba022441144bfc1c44a2b58db7dde34e;p=clitk.git diff --git a/segmentation/clitkExtractLymphStation_Supports.txx b/segmentation/clitkExtractLymphStation_Supports.txx index 1008ff4..f480d84 100644 --- a/segmentation/clitkExtractLymphStation_Supports.txx +++ b/segmentation/clitkExtractLymphStation_Supports.txx @@ -8,96 +8,76 @@ void clitk::ExtractLymphStationsFilter:: ExtractStationSupports() { - DD("ExtractStationSupports"); - // Get initial Mediastinum - m_Working_Support = m_Mediastinum = 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"); - GetAFDB()->SetImageFilename("Support_Inf_Carina", "seg/Support_Inf_Carina.mhd"); - writeImage(m_Support_Superior_to_Carina, "seg/Support_Sup_Carina.mhd"); - GetAFDB()->SetImageFilename("Support_Sup_Carina", "seg/Support_Sup_Carina.mhd"); + m_Working_Support = m_Mediastinum = this->GetAFDB()->template GetImage("Mediastinum", true); + + // Remove some computed structures + this->GetAFDB()->RemoveTag("CarinaZ"); + this->GetAFDB()->RemoveTag("ApexOfTheChestZ"); + + // Superior and inferior limits. + Support_SI_Limit("inferior", "Sup_to_Carina", "inferior", "Carina", 0); + Support_SI_Limit("superior", "Inf_to_Carina", "inferior", "Carina", m_Working_Support->GetSpacing()[2]); + + // Read all support limits in a file and apply them + ReadSupportLimits(GetSupportLimitsFilename()); + for(unsigned int i=0; iGetSpacing()[2]); + } // 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(); + Support_Post_S2S4(); + + // S3P : "the anterior border is an imaginary horizontal line + // extending along the posterior wall of the trachea" + StartNewStep("[Support] Ant limits of S3P with trachea"); + m_ListOfSupports["S3P"] = LimitsWithTrachea(m_ListOfSupports["S3P"], 1, 0, 10); + + // S3A : "Posteriorly, the station is limited by station 2R and 2L, + // but excludes the great vessels. An imaginary line joins the + // midpoint of the vessel in the anterior to posterior plane. It is + // here that station 2 contacts station 3a" ===> here limit with + // trachea only + StartNewStep("[Support] Ant limits of S3A with trachea"); + m_ListOfSupports["S3A"] = LimitsWithTrachea(m_ListOfSupports["S3A"], 1, 0, -10); + + // S1RL - posterior limits when SI overlap with S3P + Support_Post_S1_S3P(); - // S5, S6 - Support_S5(); - Support_S6(); + // S1RL - posterior limits with S2RL above sternal notch + Support_Post_S1_Ant_S2RL(); + // I will do it later // 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); + m_ListOfSupports["S7"] = clitk::Clone(m_ListOfSupports["Inf_to_Carina"]); + m_ListOfSupports["S8"] = clitk::Clone(m_ListOfSupports["Inf_to_Carina"]); + m_ListOfSupports["S9"] = clitk::Clone(m_ListOfSupports["Inf_to_Carina"]); + m_ListOfSupports["S10"] = clitk::Clone(m_ListOfSupports["Inf_to_Carina"]); + m_ListOfSupports["S11"] = clitk::Clone(m_ListOfSupports["Inf_to_Carina"]); // Store image filenames into AFDB - writeImage(m_ListOfSupports["S1R"], "seg/Support_S1R.mhd"); - GetAFDB()->SetImageFilename("Support_S1R", "seg/Support_S1R.mhd"); - writeImage(m_ListOfSupports["S1L"], "seg/Support_S1L.mhd"); - GetAFDB()->SetImageFilename("Support_S1L", "seg/Support_S1L.mhd"); - - writeImage(m_ListOfSupports["S2L"], "seg/Support_S2L.mhd"); - GetAFDB()->SetImageFilename("Support_S2L", "seg/Support_S2L.mhd"); - writeImage(m_ListOfSupports["S2R"], "seg/Support_S2R.mhd"); - GetAFDB()->SetImageFilename("Support_S2R", "seg/Support_S2R.mhd"); - - writeImage(m_ListOfSupports["S3P"], "seg/Support_S3P.mhd"); - GetAFDB()->SetImageFilename("Support_S3P", "seg/Support_S3P.mhd"); - writeImage(m_ListOfSupports["S3A"], "seg/Support_S3A.mhd"); - GetAFDB()->SetImageFilename("Support_S3A", "seg/Support_S3A.mhd"); - - writeImage(m_ListOfSupports["S4L"], "seg/Support_S4L.mhd"); - GetAFDB()->SetImageFilename("Support_S4L", "seg/Support_S4L.mhd"); - writeImage(m_ListOfSupports["S4R"], "seg/Support_S4R.mhd"); - GetAFDB()->SetImageFilename("Support_S4R", "seg/Support_S4R.mhd"); - - writeImage(m_ListOfSupports["S5"], "seg/Support_S5.mhd"); - GetAFDB()->SetImageFilename("Support_S5", "seg/Support_S5.mhd"); - writeImage(m_ListOfSupports["S6"], "seg/Support_S6.mhd"); - GetAFDB()->SetImageFilename("Support_S6", "seg/Support_S6.mhd"); - - writeImage(m_ListOfSupports["S7"], "seg/Support_S7.mhd"); - GetAFDB()->SetImageFilename("Support_S7", "seg/Support_S7.mhd"); - - writeImage(m_ListOfSupports["S8"], "seg/Support_S8.mhd"); - GetAFDB()->SetImageFilename("Support_S8", "seg/Support_S8.mhd"); - - writeImage(m_ListOfSupports["S9"], "seg/Support_S9.mhd"); - GetAFDB()->SetImageFilename("Support_S9", "seg/Support_S9.mhd"); - - writeImage(m_ListOfSupports["S10"], "seg/Support_S10.mhd"); - GetAFDB()->SetImageFilename("Support_S10", "seg/Support_S10.mhd"); - - writeImage(m_ListOfSupports["S11"], "seg/Support_S11.mhd"); - GetAFDB()->SetImageFilename("Support_S11", "seg/Support_S11.mhd"); + WriteImageSupport("S1R"); WriteImageSupport("S1L"); + WriteImageSupport("S2R"); WriteImageSupport("S2L"); + WriteImageSupport("S3A"); WriteImageSupport("S3P"); + WriteImageSupport("S4R"); WriteImageSupport("S4L"); + WriteImageSupport("S5"); + WriteImageSupport("S6"); + WriteImageSupport("S7"); + WriteImageSupport("S8"); + WriteImageSupport("S9"); + WriteImageSupport("S10"); + WriteImageSupport("S11"); WriteAFDB(); } //-------------------------------------------------------------------- @@ -107,31 +87,143 @@ ExtractStationSupports() template void clitk::ExtractLymphStationsFilter:: -Support_SupInf_S1RL() +Support_SI_Limit(const std::string station_limit, const std::string station, + const std::string structure_limit, const std::string structure, + const double offset) { - // 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 + if (!GetCheckSupportFlag()) + StartNewStep("[Support] "+station_limit+" limit of "+station+" is "+structure_limit+" limit of "+structure); - => apex / manubrium = up Sternum - */ - m_Working_Support = m_ListOfSupports["Support_Superior_to_Carina"]; - MaskImagePointer Sternum = 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; + // Check + if ((station_limit != "superior") && (station_limit != "inferior")) { + clitkExceptionMacro("Error station_limit must be 'inferior' or 'superior', not '"<< station_limit); + } + if ((structure_limit != "superior") && (structure_limit != "inferior")) { + clitkExceptionMacro("Error structure_limit must be 'inferior' or 'superior', not '"<< structure_limit); + } + + // Get current support + if (m_ListOfSupports.find(station) == m_ListOfSupports.end()) { + // std::cerr << "Warning: support " << station << " not initialized" << std::endl; + m_ListOfSupports[station] = m_Mediastinum; + } + m_Working_Support = m_ListOfSupports[station]; + + // Get structure or structureZ + double z=0.; + int found=0; + std::string file; + + // Try to load structure and compute extrema point + if (this->GetAFDB()->TagExist(structure)) { + MaskImagePointer Structure = this->GetAFDB()->template GetImage (structure); + file = this->GetAFDB()->GetTagValue(structure); + MaskImagePointType p; + p[0] = p[1] = p[2] = 0.0; // to avoid warning + if (structure_limit == "superior") + clitk::FindExtremaPointInAGivenDirection(Structure, GetBackgroundValue(), 2, false, p); + else + clitk::FindExtremaPointInAGivenDirection(Structure, GetBackgroundValue(), 2, true, p); + z = p[2]; + found=1; + } + + // Try to load structureZ + if ((found==0) && (this->GetAFDB()->TagExist(structure+"Z"))) { + z = this->GetAFDB()->GetDouble(structure+"Z"); + found=2; + } + + // Try to load structurePoint + if ((found==0) && (this->GetAFDB()->TagExist(structure+"Point"))) { + MaskImagePointType p; + this->GetAFDB()->GetPoint3D(structure+"Point", p); + z = p[2]; + found=3; + } + + // Try to see if it is an already computed support + if (found==0) { + if (m_ListOfSupports.find(structure) != m_ListOfSupports.end()) { + MaskImagePointer Structure = m_ListOfSupports[structure]; + MaskImagePointType p; + if (structure_limit == "superior") + clitk::FindExtremaPointInAGivenDirection(Structure, GetBackgroundValue(), 2, false, p); + else + clitk::FindExtremaPointInAGivenDirection(Structure, GetBackgroundValue(), 2, true, p); + z = p[2]; + found=4; + } + } + + // Try special case : "FindApexOfTheChest" + if (structure == "FindApexOfTheChest") { + z = FindApexOfTheChest(); + found=5; + } + if (structure == "FindInferiorBorderOfAorticArch") { + z = FindInferiorBorderOfAorticArch(); + found=6; + } + if (structure == "FindSuperiorBorderOfAorticArch") { + z = FindSuperiorBorderOfAorticArch(); + found=6; + } + + // If we find anything + if (found == 0) { + std::cerr << "ERROR : I could not find " << structure << " nor " << structure << "Z nor " + << structure << "Point" << std::endl; + exit(EXIT_FAILURE); + } + + // Apply offset + z += offset; + + // Remove Lower or greater + if (station_limit == "inferior") { + m_Working_Support = + clitk::CropImageRemoveLowerThan(m_Working_Support, 2, z, true, GetBackgroundValue()); + } + else { + m_Working_Support = + clitk::CropImageRemoveGreaterThan(m_Working_Support, 2, z, true, GetBackgroundValue()); + } + + // Check: if reference station is given, display information + if (GetCheckSupportFlag()) { + try { + MaskImagePointer Ref = this->GetAFDB()->template GetImage (station+"_Ref"); + MaskImagePointType p_support; + MaskImagePointType p_ref; + if (station_limit == "superior") { + clitk::FindExtremaPointInAGivenDirection(Ref, GetBackgroundValue(), 2, false, p_ref); + clitk::FindExtremaPointInAGivenDirection(m_Working_Support, GetBackgroundValue(), 2, false, p_support); + } + else { + clitk::FindExtremaPointInAGivenDirection(Ref, GetBackgroundValue(), 2, true, p_ref); + clitk::FindExtremaPointInAGivenDirection(m_Working_Support, GetBackgroundValue(), 2, true, p_support); + } + std::ostringstream os; + os << "[Support] \t" << station << "\t" << station_limit << " " + << "Z = " << z << std::setprecision(2) << std::fixed + << "\tSupport = " << p_support[2] + << "\tRef = " << p_ref[2] + << "\tdiff = " << p_support[2]-p_ref[2] << "\t" + << structure << " " << structure_limit; + if (found==1) os << " (S "+file+")"; + if (found==2) os << " (Z)"; + if (found==3) os << " (P)"; + if (found==4) os << " (p)"; + if (found==5) os << " (Apex)"; + if (found==6) os << " (AorticArch)"; + StartNewStep(os.str()); + } catch(clitk::ExceptionObject e) { } + } + + // Set support + m_ListOfSupports[station] = m_Working_Support; + StopCurrentStep(m_Working_Support); } //-------------------------------------------------------------------- @@ -142,13 +234,18 @@ void clitk::ExtractLymphStationsFilter:: Support_LeftRight_S1R_S1L() { + /* + Medially, station 1R and 1L are separated by the midline of the + trachea, whilst excluding the thyroid gland. + */ + // Step S1RL : Left-Right StartNewStep("[Support] Left-Right S1R S1L"); std::vector A; std::vector B; // Search for centroid positions of trachea - MaskImagePointer Trachea = GetAFDB()->template GetImage ("Trachea"); - MaskImagePointer S1RL = m_ListOfSupports["S1RL"]; + MaskImagePointer Trachea = this->GetAFDB()->template GetImage ("Trachea"); + MaskImagePointer S1RL = m_ListOfSupports["S1R"]; Trachea = clitk::ResizeImageLike(Trachea, S1RL, GetBackgroundValue()); std::vector slices; clitk::ExtractSlices(Trachea, 2, slices); @@ -172,65 +269,20 @@ Support_LeftRight_S1R_S1L() // Right part clitk::SliceBySliceSetBackgroundFromLineSeparation(S1R, A, B, - GetBackgroundValue(), 0, 10); + GetBackgroundValue(), 0, -10); S1R = clitk::AutoCrop(S1R, GetBackgroundValue()); m_ListOfSupports["S1R"] = S1R; // Left part clitk::SliceBySliceSetBackgroundFromLineSeparation(S1L, A, B, - GetBackgroundValue(), 0, -10); + GetBackgroundValue(), 0, 10); S1L = clitk::AutoCrop(S1L, GetBackgroundValue()); m_ListOfSupports["S1L"] = S1L; + StopCurrentStep(m_ListOfSupports["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 = GetAFDB()->template GetImage("BrachioCephalicVein"); - MaskImagePointType p; - clitk::FindExtremaPointInAGivenDirection(BrachioCephalicVein, GetBackgroundValue(), 2, true, p); - // I add slightly more than a slice - double CaudalMarginOfLeftBrachiocephalicVeinZ=p[2]+ 1.1*m_Working_Support->GetSpacing()[2]; - GetAFDB()->SetDouble("CaudalMarginOfLeftBrachiocephalicVeinZ", CaudalMarginOfLeftBrachiocephalicVeinZ); - MaskImagePointer S2R = - clitk::CropImageRemoveLowerThan(m_Working_Support, 2, - CaudalMarginOfLeftBrachiocephalicVeinZ, true, - GetBackgroundValue()); - m_ListOfSupports["S2R"] = S2R; - - // S2L : Top Of Aortic Arch - MaskImagePointer Aorta = GetAFDB()->template GetImage("Aorta"); - clitk::FindExtremaPointInAGivenDirection(Aorta, GetBackgroundValue(), 2, false, p); - // I add slightly more than a slice - double TopOfAorticArchZ=p[2]+ 1.1*m_Working_Support->GetSpacing()[2]; - GetAFDB()->SetDouble("TopOfAorticArchZ", TopOfAorticArchZ); - - MaskImagePointer S2L = - clitk::CropImageRemoveLowerThan(m_Working_Support, 2, - TopOfAorticArchZ, true, - GetBackgroundValue()); - m_ListOfSupports["S2L"] = S2L; -} -//-------------------------------------------------------------------- - - - - //-------------------------------------------------------------------- template void @@ -238,78 +290,25 @@ 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." + /* 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 in the IASLC definition + dividing 2R from 2L, from the midline to the left lateral + tracheal border. This is represented in the atlas as a vertical + line passing tangentially along the left lateral tracheal 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); + S2R = clitk::AutoCrop(S2R, GetBackgroundValue()); + S2L = clitk::AutoCrop(S2L, GetBackgroundValue()); m_ListOfSupports["S2R"] = S2R; m_ListOfSupports["S2L"] = S2L; - 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, remove 2R and 2L - MaskImagePointer S4RL = clitk::Clone(m_Working_Support); - MaskImagePointer S2R = m_ListOfSupports["S2R"]; - MaskImagePointer S2L = m_ListOfSupports["S2L"]; - clitk::AndNot(S4RL, S2R, GetBackgroundValue()); - clitk::AndNot(S4RL, S2L, GetBackgroundValue()); - S4RL = clitk::AutoCrop(S4RL, GetBackgroundValue()); - - // Copy, stop 4R at AzygousVein and 4L at LeftPulmonaryArtery - MaskImagePointer S4R = clitk::Clone(S4RL); - MaskImagePointer S4L = clitk::Clone(S4RL); - - // Get AzygousVein and limit according to LowerBorderAzygousVein - MaskImagePointer LowerBorderAzygousVein - = GetAFDB()->template GetImage("LowerBorderAzygousVein"); - std::vector c; - clitk::ComputeCentroids(LowerBorderAzygousVein, GetBackgroundValue(), c); - S4R = clitk::CropImageRemoveLowerThan(S4RL, 2, - c[1][2], true, GetBackgroundValue()); - S4R = clitk::AutoCrop(S4R, GetBackgroundValue()); - m_ListOfSupports["S4R"] = S4R; - - - // Limit according to LeftPulmonaryArtery - MaskImagePointer LeftPulmonaryArtery - = GetAFDB()->template GetImage("LeftPulmonaryArtery"); - MaskImagePointType p; - clitk::FindExtremaPointInAGivenDirection(LeftPulmonaryArtery, GetBackgroundValue(), - 2, false, p); - S4L = clitk::CropImageRemoveLowerThan(S4RL, 2, - p[2], true, GetBackgroundValue()); - S4L = clitk::AutoCrop(S4L, GetBackgroundValue()); - m_ListOfSupports["S4L"] = S4L; + this->GetAFDB()->template ReleaseImage("Trachea"); } //-------------------------------------------------------------------- @@ -321,15 +320,10 @@ 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 + /* + The medial border of station 4R is defined as an imaginary line + running vertically from the left lateral tracheal border. This is + the same medial border as was described for station 2R. */ StartNewStep("[Support] Left Right separation of 4R/4L"); @@ -366,7 +360,7 @@ LimitsWithTrachea(MaskImageType * input, int extremaDirection, int lineDirection the offset */ // Read the trachea - MaskImagePointer Trachea = GetAFDB()->template GetImage("Trachea"); + MaskImagePointer Trachea = this->GetAFDB()->template GetImage("Trachea"); // Find extrema post positions std::vector tracheaLeftPositionsA; @@ -387,7 +381,7 @@ LimitsWithTrachea(MaskImageType * input, int extremaDirection, int lineDirection GetBackgroundValue(), 2, extremaDirection, false, // Left lineDirection, // Vertical line - 1, // margins + -1, // margins tracheaLeftPositionsA, tracheaLeftPositionsB); // Do not consider trachea above the limit @@ -417,25 +411,25 @@ LimitsWithTrachea(MaskImageType * input, int extremaDirection, int lineDirection template void clitk::ExtractLymphStationsFilter:: -Support_Post_S1S2S4() +Support_Post_S2S4() { - StartNewStep("[Support] Post limits of S1RL, S2RL, S4RL"); + StartNewStep("[Support] Post limits of S2RL, S4RL"); double m_ApexOfTheChest = FindApexOfTheChest(); // Post limits with Trachea - MaskImagePointer S1R = m_ListOfSupports["S1R"]; - MaskImagePointer S1L = m_ListOfSupports["S1L"]; + // 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); + // m_ListOfSupports["S1R"] = LimitsWithTrachea(S1L, 1, 0, -10, m_ApexOfTheChest); + // m_ListOfSupports["S1L"] = LimitsWithTrachea(S1R, 1, 0, -10, m_ApexOfTheChest); + m_ListOfSupports["S2R"] = LimitsWithTrachea(S2R, 1, 0, -10, m_ApexOfTheChest); + m_ListOfSupports["S2L"] = LimitsWithTrachea(S2L, 1, 0, -10, m_ApexOfTheChest); + m_ListOfSupports["S4R"] = LimitsWithTrachea(S4R, 1, 0, -10, m_ApexOfTheChest); + m_ListOfSupports["S4L"] = LimitsWithTrachea(S4L, 1, 0, -10, m_ApexOfTheChest); } //-------------------------------------------------------------------- @@ -444,54 +438,64 @@ Support_Post_S1S2S4() template void clitk::ExtractLymphStationsFilter:: -Support_S3P() +Support_Post_S1_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"]); + StartNewStep("[Support] If S1RL and S3P have Sup-Inf overlap, define S1RL posterior limits with S3P anterior limits (post wall trachea)"); - // 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; -} -//-------------------------------------------------------------------- + // Get current supports + MaskImagePointer S1R = m_ListOfSupports["S1R"]; + MaskImagePointer S1L = m_ListOfSupports["S1L"]; + // Find extrema ant positions for 3P + std::vector A; + std::vector B; -//-------------------------------------------------------------------- -template -void -clitk::ExtractLymphStationsFilter:: -Support_S3A() -{ - StartNewStep("[Support] Sup-Inf and Post limits for S3A"); + // Crop S3P like S1R + MaskImagePointer S3P = clitk::Clone(m_ListOfSupports["S3P"]); + S3P = clitk::ResizeImageLike(S3P, S1R, GetBackgroundValue()); - // Initial S3A support - MaskImagePointer S3A = clitk::Clone(m_ListOfSupports["Support_Superior_to_Carina"]); + // Slice by slice, build the separation line + clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition(S3P, + GetBackgroundValue(), 2, + 1, true, // Ant-Post + 0, // Horizontal line + 0, // margins + A, B); - // Stop at Lung Apex or like S2/S1 (upper border Sternum - manubrium) ? + // clitk::WriteListOfLandmarks(A, "A-S1S3P.txt"); + // clitk::WriteListOfLandmarks(B, "B-S1S3P.txt"); - //double m_ApexOfTheChest = FindApexOfTheChest(); + // Cut post to this line + clitk::SliceBySliceSetBackgroundFromLineSeparation(S1R, A, B, + GetBackgroundValue(), + 1, -10); + + // Crop S3P like S1L (Redo for S1L) + S3P = clitk::Clone(m_ListOfSupports["S3P"]); + S3P = clitk::ResizeImageLike(S3P, S1L, GetBackgroundValue()); + + // Slice by slice, build the separation line + A.clear(); + B.clear(); + clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition(S3P, + GetBackgroundValue(), 2, + 1, true, // Ant-Post + 0, // Horizontal line + 0, // margins + A, B); + // Cut post to this line + clitk::SliceBySliceSetBackgroundFromLineSeparation(S1L, A, B, + GetBackgroundValue(), + 1, -10); - MaskImagePointer Sternum = GetAFDB()->template GetImage ("Sternum"); - MaskImagePointType p; - p[0] = p[1] = p[2] = 0.0; // to avoid warning - clitk::FindExtremaPointInAGivenDirection(Sternum, GetBackgroundValue(), 2, false, p); + // Crop both images + S1R = clitk::AutoCrop(S1R, GetBackgroundValue()); + S1L = clitk::AutoCrop(S1L, GetBackgroundValue()); - 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; + StopCurrentStep(S1R); + + m_ListOfSupports["S1R"] = S1R; + m_ListOfSupports["S1L"] = S1L; } //-------------------------------------------------------------------- @@ -500,49 +504,91 @@ Support_S3A() template void clitk::ExtractLymphStationsFilter:: -Support_S5() +Support_Post_S1_Ant_S2RL() { - StartNewStep("[Support] Sup-Inf limits S5 with aorta"); - - // Initial S5 support - MaskImagePointer S5 = clitk::Clone(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 = GetAFDB()->template GetImage("PulmonaryTrunk"); - MaskImagePointType p; - p[0] = p[1] = p[2] = 0.0; // to avoid warning - clitk::FindExtremaPointInAGivenDirection(PulmonaryTrunk, GetBackgroundValue(), 2, false, p); + StartNewStep("[Support] Define S1RL posterior limits with S2RL anterior limits when overlap"); + + // Get RightLung + MaskImagePointer RightLung = this->GetAFDB()->template GetImage("RightLung"); + + // Find common area between S1 and S2 + MaskImagePointType p_min; + MaskImagePointType p_max; + clitk::FindExtremaPointInAGivenDirection(m_ListOfSupports["S2R"], + GetBackgroundValue(), 2, false, p_max); + clitk::FindExtremaPointInAGivenDirection(m_ListOfSupports["S1R"], + GetBackgroundValue(), 2, true, p_min); + p_min[2] -= RightLung->GetSpacing()[2]; // consider the slice below (remove lower or equal) + p_max[2] += RightLung->GetSpacing()[2]; // consider the slice abov (remove greater or equal) - // 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"]); + if (p_min[2] > p_max[2]) { + + // Crop RightLung + RightLung = clitk::Clone(RightLung); + RightLung = clitk::ResizeImageLike(RightLung, m_ListOfSupports["S1R"], GetBackgroundValue()); + RightLung = clitk::CropImageRemoveLowerThan(RightLung, 2, p_min[2], true, GetBackgroundValue()); + RightLung = clitk::CropImageRemoveGreaterThan(RightLung, 2, p_max[2], true, GetBackgroundValue()); + + // Find extrema ant positions for RightLung + std::vector A; + std::vector B; + clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition(RightLung, + GetBackgroundValue(), 2, + 1, true, // Ant-Post + 0, // Horizontal line + 0, // margins + A, B); + clitk::SliceBySliceSetBackgroundFromLineSeparation(m_ListOfSupports["S1R"], + A, B, + GetBackgroundValue(), + 1, -10); + // I add one pixel to abupt S2R to S1R + for(int i=0; iGetSpacing()[1]; + B[i][1] -= RightLung->GetSpacing()[1]; + } + clitk::SliceBySliceSetBackgroundFromLineSeparation(m_ListOfSupports["S2R"], + A, B, + GetBackgroundValue(), + 1, 10); + } - // Inf Sup limits with Aorta - double sup = FindSuperiorBorderOfAorticArch(); - double inf = FindInferiorBorderOfAorticArch(); + // Get LeftLung, crop + MaskImagePointer LeftLung = this->GetAFDB()->template GetImage("LeftLung"); + clitk::FindExtremaPointInAGivenDirection(m_ListOfSupports["S2L"], + GetBackgroundValue(), 2, false, p_max); + clitk::FindExtremaPointInAGivenDirection(m_ListOfSupports["S1L"], + GetBackgroundValue(), 2, true, p_min); + p_min[2] -= LeftLung->GetSpacing()[2]; // consider the slice below (remove lower or equal) + p_max[2] += LeftLung->GetSpacing()[2]; // consider the slice abov (remove greater or equal) - // Cut Sup/Inf - S6 = clitk::CropImageAlongOneAxis(S6, 2, inf, sup, true, GetBackgroundValue()); - - m_ListOfSupports["S6"] = S6; + if (p_min[2] > p_max[2]) { + LeftLung = clitk::ResizeImageLike(LeftLung, m_ListOfSupports["S1L"], GetBackgroundValue()); + LeftLung = clitk::CropImageRemoveLowerThan(LeftLung, 2, p_min[2], true, GetBackgroundValue()); + LeftLung = clitk::CropImageRemoveGreaterThan(LeftLung, 2, p_max[2], true, GetBackgroundValue()); + + // Find extrema ant positions for LeftLung + std::vector A; + std::vector B; + clitk::SliceBySliceBuildLineSegmentAccordingToExtremaPosition(LeftLung, + GetBackgroundValue(), 2, + 1, true, // Ant-Post + 0, // Horizontal line + 0, // margins + A, B); + clitk::SliceBySliceSetBackgroundFromLineSeparation(m_ListOfSupports["S1L"], + A, B, + GetBackgroundValue(), + 1, -10); + // I add one pixel to abupt S2R to S1R + for(int i=0; iGetSpacing()[1]; + B[i][1] -= LeftLung->GetSpacing()[1]; + } + clitk::SliceBySliceSetBackgroundFromLineSeparation(m_ListOfSupports["S2L"], + A, B, + GetBackgroundValue(), + 1, 10); + } } //-------------------------------------------------------------------- -