X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=segmentation%2FclitkExtractLymphStationsFilter.txx;h=98768b278bfdaf105662efb118684db4d5c2713d;hb=7f7c290c75d4917446f8751856ae7d450f58a6f0;hp=aab78d85c01730c9778c4be1cf81d99c28726728;hpb=6e16222234a90c6079a8f4696c92de7349a496bb;p=clitk.git diff --git a/segmentation/clitkExtractLymphStationsFilter.txx b/segmentation/clitkExtractLymphStationsFilter.txx index aab78d8..98768b2 100644 --- a/segmentation/clitkExtractLymphStationsFilter.txx +++ b/segmentation/clitkExtractLymphStationsFilter.txx @@ -27,6 +27,7 @@ #include "clitkAutoCropFilter.h" #include "clitkSegmentationUtils.h" #include "clitkSliceBySliceRelativePositionFilter.h" +#include "clitkMeshToBinaryImageFilter.h" // itk #include @@ -34,26 +35,42 @@ #include #include #include +#include #include // itk ENST #include "RelativePositionPropImageFilter.h" +// vtk +#include +#include +#include + //-------------------------------------------------------------------- template clitk::ExtractLymphStationsFilter:: ExtractLymphStationsFilter(): - clitk::FilterBase(), - itk::ImageToImageFilter() + clitk::StructuresExtractionFilter() { this->SetNumberOfRequiredInputs(1); - SetBackgroundValueMediastinum(0); - SetBackgroundValueTrachea(0); SetBackgroundValue(0); SetForegroundValue(1); + ForceSupportsFlagOn(); + SetSupportLimitsFilename("none"); + CheckSupportFlagOff(); + + // Default values + ExtractStation_3P_SetDefaultValues(); + ExtractStation_2RL_SetDefaultValues(); + ExtractStation_3A_SetDefaultValues(); + ExtractStation_1RL_SetDefaultValues(); + ExtractStation_4RL_SetDefaultValues(); + ExtractStation_5_SetDefaultValues(); + ExtractStation_6_SetDefaultValues(); - SetIntermediateSpacing(6); - SetFuzzyThreshold1(0.6); + // TODO + ExtractStation_7_SetDefaultValues(); + ExtractStation_8_SetDefaultValues(); } //-------------------------------------------------------------------- @@ -62,13 +79,80 @@ ExtractLymphStationsFilter(): template void clitk::ExtractLymphStationsFilter:: -SetInputMediastinumLabelImage(const TImageType * image, ImagePixelType bg) { - this->SetNthInput(0, const_cast(image)); - m_BackgroundValueMediastinum = bg; - SetCarenaZPositionInMM(image->GetOrigin()[2]+image->GetLargestPossibleRegion().GetSize()[2]*image->GetSpacing()[2]); - SetMiddleLobeBronchusZPositionInMM(image->GetOrigin()[2]); - // DD(m_CarenaZPositionInMM); -// DD(m_MiddleLobeBronchusZPositionInMM); +GenerateOutputInformation() { + // Get inputs + this->LoadAFDB(); + m_Input = dynamic_cast(itk::ProcessObject::GetInput(0)); + m_Mediastinum = this->GetAFDB()->template GetImage ("Mediastinum"); + + // Clean some computer landmarks to force the recomputation + // FIXME -> to put elsewhere ? + this->GetAFDB()->RemoveTag("AntPostVesselsSeparation"); + + // Must I compute the supports ? + bool supportsExist = true; + if (!GetForceSupportsFlag()) { + try { + m_ListOfSupports["S1R"] = this->GetAFDB()->template GetImage("Support_S1R"); + m_ListOfSupports["S1L"] = this->GetAFDB()->template GetImage("Support_S1L"); + m_ListOfSupports["S2R"] = this->GetAFDB()->template GetImage("Support_S2R"); + m_ListOfSupports["S2L"] = this->GetAFDB()->template GetImage("Support_S2L"); + m_ListOfSupports["S4R"] = this->GetAFDB()->template GetImage("Support_S4R"); + m_ListOfSupports["S4L"] = this->GetAFDB()->template GetImage("Support_S4L"); + + m_ListOfSupports["S3A"] = this->GetAFDB()->template GetImage("Support_S3A"); + m_ListOfSupports["S3P"] = this->GetAFDB()->template GetImage("Support_S3P"); + m_ListOfSupports["S5"] = this->GetAFDB()->template GetImage("Support_S5"); + m_ListOfSupports["S6"] = this->GetAFDB()->template GetImage("Support_S6"); + m_ListOfSupports["S7"] = this->GetAFDB()->template GetImage("Support_S7"); + m_ListOfSupports["S8"] = this->GetAFDB()->template GetImage("Support_S8"); + m_ListOfSupports["S9"] = this->GetAFDB()->template GetImage("Support_S9"); + m_ListOfSupports["S10"] = this->GetAFDB()->template GetImage("Support_S10"); + m_ListOfSupports["S11"] = this->GetAFDB()->template GetImage("Support_S11"); + } catch(clitk::ExceptionObject o) { + supportsExist = false; + } + } + + if (!supportsExist || GetForceSupportsFlag()) { + this->StartNewStep("Supports for stations"); + this->StartSubStep(); + + // FIXME : why should I remove theses tags ??? + this->GetAFDB()->RemoveTag("CarinaZ"); + this->GetAFDB()->RemoveTag("ApexOfTheChestZ"); + this->GetAFDB()->RemoveTag("ApexOfTheChest"); + this->GetAFDB()->RemoveTag("RightBronchus"); + this->GetAFDB()->RemoveTag("LeftBronchus"); + this->GetAFDB()->RemoveTag("SuperiorBorderOfAorticArchZ"); + this->GetAFDB()->RemoveTag("SuperiorBorderOfAorticArch"); + this->GetAFDB()->RemoveTag("InferiorBorderOfAorticArchZ"); + this->GetAFDB()->RemoveTag("InferiorBorderOfAorticArch"); + ExtractStationSupports(); + this->StopSubStep(); + } + + // Extract Stations + ExtractStation_1RL(); + ExtractStation_2RL(); + ExtractStation_3P(); + ExtractStation_3A(); + ExtractStation_4R(); + ExtractStation_4L(); + ExtractStation_5(); + ExtractStation_6(); + + // ---------- todo ----------------------- + + // Extract Station8 + // ExtractStation_8(); + + // Extract Station7 + //this->StartNewStep("Station 7"); + //this->StartSubStep(); + //ExtractStation_7(); + //this->StopSubStep(); + } //-------------------------------------------------------------------- @@ -77,29 +161,174 @@ SetInputMediastinumLabelImage(const TImageType * image, ImagePixelType bg) { template void clitk::ExtractLymphStationsFilter:: -SetInputTracheaLabelImage(const TImageType * image, ImagePixelType bg) { - this->SetNthInput(1, const_cast(image)); - m_BackgroundValueTrachea = bg; +GenerateInputRequestedRegion() { + //DD("GenerateInputRequestedRegion (nothing?)"); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template -template void clitk::ExtractLymphStationsFilter:: -SetArgsInfo(ArgsInfoType mArgsInfo) +GenerateData() { + // Final Step -> graft output (if SetNthOutput => redo) + // this->GraftOutput(m_ListOfStations["8"]); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +bool +clitk::ExtractLymphStationsFilter:: +CheckForStation(std::string station) { - SetVerboseOption_GGO(mArgsInfo); - SetVerboseStep_GGO(mArgsInfo); - SetWriteStep_GGO(mArgsInfo); - SetVerboseWarningOff_GGO(mArgsInfo); - SetCarenaZPositionInMM_GGO(mArgsInfo); - SetMiddleLobeBronchusZPositionInMM_GGO(mArgsInfo); - SetIntermediateSpacing_GGO(mArgsInfo); - SetFuzzyThreshold1_GGO(mArgsInfo); - //SetBackgroundValueMediastinum_GGO(mArgsInfo); + // Compute Station name + std::string s = "Station"+station; + + + // Define the starting support + // if (GetComputeStation(station)) { + // std::cout << "Station " << station << " already exists, but re-computation forced." << std::endl; + // } + if (GetComputeStation(station)) { + m_Working_Support = m_Mediastinum = this->GetAFDB()->template GetImage("Mediastinum", true); + return true; + } + else return false; + // else { + // std::cout << "Station " << station << " found. I used it" << std::endl; + // return false; + // } + + // Check if station already exist in DB + + // FIXME -> do nothing if not on the command line. Is it what I want ? + //bool found = false; + if (this->GetAFDB()->TagExist(s)) { + m_ListOfStations[station] = this->GetAFDB()->template GetImage(s); + //found = true; + } + +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +bool +clitk::ExtractLymphStationsFilter:: +GetComputeStation(std::string station) +{ + return (m_ComputeStationMap.find(station) != m_ComputeStationMap.end()); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +AddComputeStation(std::string station) +{ + m_ComputeStationMap[station] = true; +} +//-------------------------------------------------------------------- + + + +//-------------------------------------------------------------------- +template +class comparePointsX { +public: + bool operator() (PointType i, PointType j) { return (i[0] +class comparePointsWithAngle { +public: + bool operator() (PairType i, PairType j) { return (i.second < j.second); } +}; +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void HypercubeCorners(std::vector > & out) { + std::vector > previous; + HypercubeCorners(previous); + out.resize(previous.size()*2); + for(unsigned int i=0; i p; + if (i +void HypercubeCorners<1>(std::vector > & out) { + out.resize(2); + out[0][0] = 0; + out[1][0] = 1; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void ComputeImageBoundariesCoordinates(typename ImageType::Pointer image, + std::vector & bounds) +{ + // Get image max/min coordinates + const unsigned int dim=ImageType::ImageDimension; + typedef typename ImageType::PointType PointType; + typedef typename ImageType::IndexType IndexType; + PointType min_c, max_c; + IndexType min_i, max_i; + min_i = image->GetLargestPossibleRegion().GetIndex(); + for(unsigned int i=0; iGetLargestPossibleRegion().GetSize()[i] + min_i[i]; + image->TransformIndexToPhysicalPoint(min_i, min_c); + image->TransformIndexToPhysicalPoint(max_i, max_c); + + // Get corners coordinates + HypercubeCorners(bounds); + for(unsigned int i=0; i +void +clitk::ExtractLymphStationsFilter:: +Remove_Structures(std::string station, std::string s) +{ + try { + this->StartNewStep("[Station"+station+"] Remove "+s); + MaskImagePointer Structure = this->GetAFDB()->template GetImage(s); + clitk::AndNot(m_Working_Support, Structure, GetBackgroundValue()); + } + catch(clitk::ExceptionObject e) { + std::cout << s << " not found, skip." << std::endl; + } } //-------------------------------------------------------------------- @@ -108,193 +337,1142 @@ SetArgsInfo(ArgsInfoType mArgsInfo) template void clitk::ExtractLymphStationsFilter:: -GenerateOutputInformation() { - // Superclass::GenerateOutputInformation(); +SetFuzzyThreshold(std::string station, std::string tag, double value) +{ + m_FuzzyThreshold[station][tag] = value; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +SetThreshold(std::string station, std::string tag, double value) +{ + m_Threshold[station][tag] = value; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +double +clitk::ExtractLymphStationsFilter:: +GetFuzzyThreshold(std::string station, std::string tag) +{ + if (m_FuzzyThreshold.find(station) == m_FuzzyThreshold.end()) { + clitkExceptionMacro("Could not find options for station "+station+" in the list (while searching for tag "+tag+")."); + return 0.0; + } + + if (m_FuzzyThreshold[station].find(tag) == m_FuzzyThreshold[station].end()) { + clitkExceptionMacro("Could not find options "+tag+" in the list of FuzzyThreshold for station "+station+"."); + return 0.0; + } + + return m_FuzzyThreshold[station][tag]; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +double +clitk::ExtractLymphStationsFilter:: +GetThreshold(std::string station, std::string tag) +{ + if (m_Threshold.find(station) == m_Threshold.end()) { + clitkExceptionMacro("Could not find options for station "+station+" in the list (while searching for tag "+tag+")."); + return 0.0; + } + + if (m_Threshold[station].find(tag) == m_Threshold[station].end()) { + clitkExceptionMacro("Could not find options "+tag+" in the list of Threshold for station "+station+"."); + return 0.0; + } + + return m_Threshold[station][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 - // Get input - m_mediastinum = dynamic_cast(itk::ProcessObject::GetInput(0)); - m_trachea = dynamic_cast(itk::ProcessObject::GetInput(1)); + try { + this->GetAFDB()->GetPoint3D("LineForS7S8Separation_Begin", A); + this->GetAFDB()->GetPoint3D("LineForS7S8Separation_End", B); + } + catch(clitk::ExceptionObject & o) { - // ---------------------------------------------------------------- - // ---------------------------------------------------------------- - // Superior limit = carena - // Inferior limit = origine middle lobe bronchus - StartNewStep("Inf/Sup mediastinum limits with carena/bronchus"); - ImageRegionType region = m_mediastinum->GetLargestPossibleRegion(); DD(region); - ImageSizeType size = region.GetSize(); - ImageIndexType index = region.GetIndex(); - DD(m_CarenaZPositionInMM); - DD(m_MiddleLobeBronchusZPositionInMM); - index[2] = floor((m_MiddleLobeBronchusZPositionInMM - m_mediastinum->GetOrigin()[2]) / m_mediastinum->GetSpacing()[2]); - size[2] = ceil((m_CarenaZPositionInMM-m_MiddleLobeBronchusZPositionInMM) / m_mediastinum->GetSpacing()[2]); - region.SetSize(size); - region.SetIndex(index); - DD(region); - typedef itk::RegionOfInterestImageFilter CropFilterType; - typename CropFilterType::Pointer cropFilter = CropFilterType::New(); - cropFilter->SetInput(m_mediastinum); - cropFilter->SetRegionOfInterest(region); - cropFilter->Update(); - m_working_image = cropFilter->GetOutput(); - // Auto Crop (because following rel pos is faster) - m_working_image = clitk::AutoCrop(m_working_image, 0); - StopCurrentStep(m_working_image); - m_output = m_working_image; - - // ---------------------------------------------------------------- - // ---------------------------------------------------------------- - // Separate trachea in two CCL - StartNewStep("Separate trachea under carena"); - // DD(region); - ImageRegionType trachea_region = m_trachea->GetLargestPossibleRegion(); - for(int i=0; i<3; i++) { - index[i] = floor(((index[i]*m_mediastinum->GetSpacing()[i])+m_mediastinum->GetOrigin()[i] - -m_trachea->GetOrigin()[i])/m_trachea->GetSpacing()[i]); - // DD(index[i]); - size[i] = ceil((size[i]*m_mediastinum->GetSpacing()[i])/m_trachea->GetSpacing()[i]); - // DD(size[i]); - if (index[i] < 0) { - size[i] += index[i]; - index[i] = 0; - } - if (size[i]+index[i] > (trachea_region.GetSize()[i] + trachea_region.GetIndex()[i])) { - size[i] = trachea_region.GetSize()[i] + trachea_region.GetIndex()[i] - index[i]; + DD("FindLineForS7S8Separation"); + // Load LeftLowerLobeBronchus and get centroid point + MaskImagePointer LeftLowerLobeBronchus = + this->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 = + this->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 + this->GetAFDB()->SetPoint3D("LineForS7S8Separation_Begin", A); + this->GetAFDB()->SetPoint3D("LineForS7S8Separation_End", B); } - // DD(index); - // DD(size); - region.SetIndex(index); - region.SetSize(size); - // typedef itk::RegionOfInterestImageFilter CropFilterType; - // typename CropFilterType::Pointer - cropFilter = CropFilterType::New(); - // m_trachea.Print(std::cout); - cropFilter->SetInput(m_trachea); - cropFilter->SetRegionOfInterest(region); - cropFilter->Update(); - m_working_trachea = cropFilter->GetOutput(); - - // Labelize and consider two main labels - m_working_trachea = Labelize(m_working_trachea, 0, true, 1); - - // Detect wich label is at Left - typedef itk::ImageSliceConstIteratorWithIndex SliceIteratorType; - SliceIteratorType iter(m_working_trachea, m_working_trachea->GetLargestPossibleRegion()); - iter.SetFirstDirection(0); - iter.SetSecondDirection(1); - iter.GoToBegin(); - bool stop = false; - ImagePixelType leftLabel; - ImagePixelType rightLabel; - while (!stop) { - if (iter.Get() != m_BackgroundValueTrachea) { - // DD(iter.GetIndex()); - // DD((int)iter.Get()); - leftLabel = iter.Get(); - stop = true; - } - ++iter; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +double +clitk::ExtractLymphStationsFilter:: +FindCarina() +{ + double z; + try { + z = this->GetAFDB()->GetDouble("CarinaZ"); } - if (leftLabel == 1) rightLabel = 2; - else rightLabel = 1; - DD((int)leftLabel); - DD((int)rightLabel); + catch(clitk::ExceptionObject e) { + //DD("FindCarinaSlicePosition"); + // Get Carina + MaskImagePointer Carina = this->GetAFDB()->template GetImage("Carina"); + + // Get Centroid and Z value + std::vector centroids; + clitk::ComputeCentroids(Carina, GetBackgroundValue(), centroids); - // End step - StopCurrentStep(m_working_trachea); + // We dont need Carina structure from now + this->GetAFDB()->template ReleaseImage("Carina"); + + // Put inside the AFDB + this->GetAFDB()->SetPoint3D("CarinaPoint", centroids[1]); + this->GetAFDB()->SetDouble("CarinaZ", centroids[1][2]); + this->WriteAFDB(); + z = centroids[1][2]; + } + return z; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +double +clitk::ExtractLymphStationsFilter:: +FindApexOfTheChest() +{ + double z; + try { + z = this->GetAFDB()->GetDouble("ApexOfTheChestZ"); + } + catch(clitk::ExceptionObject e) { + + /* + //DD("FindApexOfTheChestPosition"); + MaskImagePointer Lungs = this->GetAFDB()->template GetImage("Lungs"); + MaskImagePointType p; + p[0] = p[1] = p[2] = 0.0; // to avoid warning + clitk::FindExtremaPointInAGivenDirection(Lungs, GetBackgroundValue(), 2, false, p); + + // We dont need Lungs structure from now + this->GetAFDB()->template ReleaseImage("Lungs"); + + // Put inside the AFDB + this->GetAFDB()->SetPoint3D("ApexOfTheChest", p); + p[2] -= 5; // We consider 5 mm lower + this->GetAFDB()->SetDouble("ApexOfTheChestZ", p[2]); + this->WriteAFDB(); + z = p[2]; + */ + + // the superior border becomes the more inferior of the two apices + MaskImagePointer RightLung = this->GetAFDB()->template GetImage("RightLung"); + MaskImagePointer LeftLung = this->GetAFDB()->template GetImage("LeftLung"); + MaskImagePointType pr; + MaskImagePointType pl; + clitk::FindExtremaPointInAGivenDirection(RightLung, GetBackgroundValue(), 2, false, pr); + clitk::FindExtremaPointInAGivenDirection(LeftLung, GetBackgroundValue(), 2, false, pl); + // We dont need Lungs structure from now + this->GetAFDB()->template ReleaseImage("RightLung"); + this->GetAFDB()->template ReleaseImage("LeftLung"); + // Put inside the AFDB + if (pr[2] < pl[2]) z = pr[2]; + else z = pl[2]; + this->GetAFDB()->SetDouble("ApexOfTheChestZ", z); + this->WriteAFDB(); + } + return z; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +FindLeftAndRightBronchi() +{ + try { + m_RightBronchus = this->GetAFDB()->template GetImage ("RightBronchus"); + m_LeftBronchus = this->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. - //----------------------------------------------------- - /* DD("TEST Skeleton"); - typedef itk::BinaryThinningImageFilter SkeletonFilterType; - typename SkeletonFilterType::Pointer skeletonFilter = SkeletonFilterType::New(); - skeletonFilter->SetInput(m_working_trachea); - skeletonFilter->Update(); - writeImage(skeletonFilter->GetOutput(), "skel.mhd"); - writeImage(skeletonFilter->GetThinning(), "skel2.mhd"); - */ + // Get the trachea + MaskImagePointer Trachea = this->GetAFDB()->template GetImage("Trachea"); + + // Get the Carina position + double m_CarinaZ = FindCarina(); + + // 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"); + } - //----------------------------------------------------- - StartNewStep("Left limits with bronchus (slice by slice)"); - // Select LeftLabel (set right label to 0) - ImagePointer temp = SetBackground(m_working_trachea, m_working_trachea, rightLabel, 0); - writeImage(temp, "temp1.mhd"); - - typedef clitk::SliceBySliceRelativePositionFilter SliceRelPosFilterType; - typename SliceRelPosFilterType::Pointer sliceRelPosFilter = SliceRelPosFilterType::New(); - sliceRelPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()); - sliceRelPosFilter->VerboseStepOn(); - sliceRelPosFilter->WriteStepOn(); - sliceRelPosFilter->SetInput(m_working_image); - sliceRelPosFilter->SetInputObject(temp); - sliceRelPosFilter->SetDirection(2); - sliceRelPosFilter->SetFuzzyThreshold(0.5); - sliceRelPosFilter->SetOrientationType(SliceRelPosFilterType::RelPosFilterType::RightTo); - sliceRelPosFilter->Update(); - m_working_image = sliceRelPosFilter->GetOutput(); - writeImage(m_working_image, "afterslicebyslice.mhd"); - - - //----------------------------------------------------- - StartNewStep("Right limits with bronchus (slice by slice)"); - // Select LeftLabel (set right label to 0) - temp = SetBackground(m_working_trachea, m_working_trachea, leftLabel, 0); - writeImage(temp, "temp2.mhd"); - - sliceRelPosFilter = SliceRelPosFilterType::New(); - sliceRelPosFilter->SetCurrentStepBaseId(this->GetCurrentStepId()); - sliceRelPosFilter->VerboseStepOn(); - sliceRelPosFilter->WriteStepOn(); - sliceRelPosFilter->SetInput(m_working_image); - sliceRelPosFilter->SetInputObject(temp); - sliceRelPosFilter->SetDirection(2); - sliceRelPosFilter->SetFuzzyThreshold(0.5); - sliceRelPosFilter->SetOrientationType(SliceRelPosFilterType::RelPosFilterType::LeftTo); - sliceRelPosFilter->Update(); - m_working_image = sliceRelPosFilter->GetOutput(); - writeImage(m_working_image, "afterslicebyslice.mhd"); - - - DD("end"); - m_output = m_working_image; - StopCurrentStep(m_output); - - // Set output image information (required) - ImagePointer outputImage = this->GetOutput(0); - outputImage->SetRegions(m_working_image->GetLargestPossibleRegion()); - outputImage->SetOrigin(m_working_image->GetOrigin()); - outputImage->SetRequestedRegion(m_working_image->GetLargestPossibleRegion()); - DD("end2"); + // 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 + this->GetAFDB()->template SetImage ("RightBronchus", "seg/rightBronchus.mhd", + RightBronchus, true); + this->GetAFDB()->template SetImage ("LeftBronchus", "seg/leftBronchus.mhd", + LeftBronchus, true); + } } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template -void +double clitk::ExtractLymphStationsFilter:: -GenerateInputRequestedRegion() { - DD("GenerateInputRequestedRegion"); - // Call default - Superclass::GenerateInputRequestedRegion(); - // Following needed because output region can be greater than input (trachea) - ImagePointer mediastinum = dynamic_cast(itk::ProcessObject::GetInput(0)); - ImagePointer trachea = dynamic_cast(itk::ProcessObject::GetInput(1)); - mediastinum->SetRequestedRegion(mediastinum->GetLargestPossibleRegion()); - trachea->SetRequestedRegion(trachea->GetLargestPossibleRegion()); +FindSuperiorBorderOfAorticArch() +{ + double z; + try { + z = this->GetAFDB()->GetDouble("SuperiorBorderOfAorticArchZ"); + } + catch(clitk::ExceptionObject e) { + // DD("FindSuperiorBorderOfAorticArch"); + MaskImagePointer Aorta = this->GetAFDB()->template GetImage("Aorta"); + MaskImagePointType p; + p[0] = p[1] = p[2] = 0.0; // to avoid warning + clitk::FindExtremaPointInAGivenDirection(Aorta, GetBackgroundValue(), 2, false, p); + p[2] += Aorta->GetSpacing()[2]; // the slice above + + // We dont need Lungs structure from now + this->GetAFDB()->template ReleaseImage("Aorta"); + + // Put inside the AFDB + this->GetAFDB()->SetPoint3D("SuperiorBorderOfAorticArch", p); + this->GetAFDB()->SetDouble("SuperiorBorderOfAorticArchZ", p[2]); + this->WriteAFDB(); + z = p[2]; + } + return z; } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template -void +double clitk::ExtractLymphStationsFilter:: -GenerateData() { - DD("GenerateData"); - // Final Step -> graft output (if SetNthOutput => redo) - this->GraftOutput(m_output); +FindInferiorBorderOfAorticArch() +{ + double z; + try { + z = this->GetAFDB()->GetDouble("InferiorBorderOfAorticArchZ"); + } + catch(clitk::ExceptionObject e) { + //DD("FindInferiorBorderOfAorticArch"); + MaskImagePointer Aorta = this->GetAFDB()->template GetImage("Aorta"); + std::vector slices; + clitk::ExtractSlices(Aorta, 2, slices); + bool found=false; + uint i = slices.size()-1; + while (!found) { + slices[i] = Labelize(slices[i], 0, false, 10); + std::vector c; + clitk::ComputeCentroids(slices[i], GetBackgroundValue(), c); + if (c.size()>2) { + found = true; + } + else { + i--; + } + } + MaskImageIndexType index; + index[0] = index[1] = 0.0; + index[2] = i+1; + MaskImagePointType lower; + Aorta->TransformIndexToPhysicalPoint(index, lower); + + // We dont need Lungs structure from now + this->GetAFDB()->template ReleaseImage("Aorta"); + + // Put inside the AFDB + this->GetAFDB()->SetPoint3D("InferiorBorderOfAorticArch", lower); + this->GetAFDB()->SetDouble("InferiorBorderOfAorticArchZ", lower[2]); + this->WriteAFDB(); + z = lower[2]; + } + return z; +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +typename clitk::ExtractLymphStationsFilter::MaskImagePointer +clitk::ExtractLymphStationsFilter:: +FindAntPostVesselsOLD() +{ + // ----------------------------------------------------- + /* Rod says: "The anterior border, as with the Atlas – UM, is + posterior to the vessels (right subclavian vein, left + brachiocephalic vein, right brachiocephalic vein, left subclavian + artery, left common carotid artery and brachiocephalic trunk). + These vessels are not included in the nodal station. The anterior + border is drawn to the midpoint of the vessel and an imaginary + line joins the middle of these vessels. Between the vessels, + station 2 is in contact with station 3a." */ + + // Check if no already done + bool found = true; + MaskImagePointer binarizedContour; + try { + binarizedContour = this->GetAFDB()->template GetImage ("AntPostVesselsSeparation"); + } + catch(clitk::ExceptionObject e) { + found = false; + } + if (found) { + return binarizedContour; + } + + /* Here, we consider the vessels form a kind of anterior barrier. We + link all vessels centroids and remove what is post to it. + - select the list of structure + vessel1 = BrachioCephalicArtery + vessel2 = BrachioCephalicVein (warning several CCL, keep most at Right) + vessel3 = CommonCarotidArtery + vessel4 = SubclavianArtery + other = Thyroid + other = Aorta + - crop images as needed + - slice by slice, choose the CCL and extract centroids + - slice by slice, sort according to polar angle wrt Trachea centroid. + - slice by slice, link centroids and close contour + - remove outside this contour + - merge with support + */ + + // Read structures + MaskImagePointer BrachioCephalicArtery = this->GetAFDB()->template GetImage("BrachioCephalicArtery"); + MaskImagePointer BrachioCephalicVein = this->GetAFDB()->template GetImage("BrachioCephalicVein"); + MaskImagePointer CommonCarotidArtery = this->GetAFDB()->template GetImage("CommonCarotidArtery"); + MaskImagePointer SubclavianArtery = this->GetAFDB()->template GetImage("SubclavianArtery"); + MaskImagePointer Thyroid = this->GetAFDB()->template GetImage("Thyroid"); + MaskImagePointer Aorta = this->GetAFDB()->template GetImage("Aorta"); + MaskImagePointer Trachea = this->GetAFDB()->template GetImage("Trachea"); + + // Create a temporay support + // From first slice of BrachioCephalicVein to end of 3A + MaskImagePointer support = this->GetAFDB()->template GetImage("Support_Sup_Carina"); + MaskImagePointType p; + p[0] = p[1] = p[2] = 0.0; // to avoid warning + clitk::FindExtremaPointInAGivenDirection(BrachioCephalicVein, GetBackgroundValue(), 2, true, p); + double inf = p [2]; + clitk::FindExtremaPointInAGivenDirection(this->GetAFDB()->template GetImage("Support_S3A"), + GetBackgroundValue(), 2, false, p); + double sup = p [2]; + support = clitk::CropImageAlongOneAxis(support, 2, inf, sup, + false, GetBackgroundValue()); + + // Resize all structures like support + BrachioCephalicArtery = + clitk::ResizeImageLike(BrachioCephalicArtery, support, GetBackgroundValue()); + CommonCarotidArtery = + clitk::ResizeImageLike(CommonCarotidArtery, support, GetBackgroundValue()); + SubclavianArtery = + clitk::ResizeImageLike(SubclavianArtery, support, GetBackgroundValue()); + Thyroid = + clitk::ResizeImageLike(Thyroid, support, GetBackgroundValue()); + Aorta = + clitk::ResizeImageLike(Aorta, support, GetBackgroundValue()); + BrachioCephalicVein = + clitk::ResizeImageLike(BrachioCephalicVein, support, GetBackgroundValue()); + Trachea = + clitk::ResizeImageLike(Trachea, support, GetBackgroundValue()); + + // Extract slices + std::vector slices_BrachioCephalicArtery; + clitk::ExtractSlices(BrachioCephalicArtery, 2, slices_BrachioCephalicArtery); + std::vector slices_BrachioCephalicVein; + clitk::ExtractSlices(BrachioCephalicVein, 2, slices_BrachioCephalicVein); + std::vector slices_CommonCarotidArtery; + clitk::ExtractSlices(CommonCarotidArtery, 2, slices_CommonCarotidArtery); + std::vector slices_SubclavianArtery; + clitk::ExtractSlices(SubclavianArtery, 2, slices_SubclavianArtery); + std::vector slices_Thyroid; + clitk::ExtractSlices(Thyroid, 2, slices_Thyroid); + std::vector slices_Aorta; + clitk::ExtractSlices(Aorta, 2, slices_Aorta); + std::vector slices_Trachea; + clitk::ExtractSlices(Trachea, 2, slices_Trachea); + unsigned int n= slices_BrachioCephalicArtery.size(); + + // Get the boundaries of one slice + std::vector bounds; + ComputeImageBoundariesCoordinates(slices_BrachioCephalicArtery[0], bounds); + + // For all slices, for all structures, find the centroid and build the contour + // List of 3D points (for debug) + std::vector p3D; + + vtkSmartPointer append = vtkSmartPointer::New(); + for(unsigned int i=0; i(slices_CommonCarotidArtery[i], + GetBackgroundValue(), true, 1); + slices_SubclavianArtery[i] = Labelize(slices_SubclavianArtery[i], + GetBackgroundValue(), true, 1); + slices_BrachioCephalicArtery[i] = Labelize(slices_BrachioCephalicArtery[i], + GetBackgroundValue(), true, 1); + slices_BrachioCephalicVein[i] = Labelize(slices_BrachioCephalicVein[i], + GetBackgroundValue(), true, 1); + slices_Thyroid[i] = Labelize(slices_Thyroid[i], + GetBackgroundValue(), true, 1); + slices_Aorta[i] = Labelize(slices_Aorta[i], + GetBackgroundValue(), true, 1); + + // Search centroids + std::vector points2D; + std::vector centroids1; + std::vector centroids2; + std::vector centroids3; + std::vector centroids4; + std::vector centroids5; + std::vector centroids6; + ComputeCentroids(slices_CommonCarotidArtery[i], GetBackgroundValue(), centroids1); + ComputeCentroids(slices_SubclavianArtery[i], GetBackgroundValue(), centroids2); + ComputeCentroids(slices_BrachioCephalicArtery[i], GetBackgroundValue(), centroids3); + ComputeCentroids(slices_Thyroid[i], GetBackgroundValue(), centroids4); + ComputeCentroids(slices_Aorta[i], GetBackgroundValue(), centroids5); + ComputeCentroids(slices_BrachioCephalicVein[i], GetBackgroundValue(), centroids6); + + // BrachioCephalicVein -> when it is separated into two CCL, we + // only consider the most at Right one + if (centroids6.size() > 2) { + if (centroids6[1][0] < centroids6[2][0]) centroids6.erase(centroids6.begin()+2); + else centroids6.erase(centroids6.begin()+1); + } + + // BrachioCephalicVein -> when SubclavianArtery has 2 CCL + // (BrachioCephalicArtery is divided) -> forget BrachioCephalicVein + if ((centroids3.size() ==1) && (centroids2.size() > 2)) { + centroids6.clear(); + } + + for(unsigned int j=1; j centroids_trachea; + ComputeCentroids(slices_Trachea[i], GetBackgroundValue(), centroids_trachea); + typedef std::pair PointAngleType; + std::vector angles; + for(unsigned int j=0; j0) angle = atan(y/x); + if ((x<0) && (y>=0)) angle = atan(y/x)+M_PI; + if ((x<0) && (y<0)) angle = atan(y/x)-M_PI; + if (x==0) { + if (y>0) angle = M_PI/2.0; + if (y<0) angle = -M_PI/2.0; + if (y==0) angle = 0; + } + angle = clitk::rad2deg(angle); + // Angle is [-180;180] wrt the X axis. We change the X axis to + // be the vertical line, because we want to sort from Right to + // Left from Post to Ant. + if (angle>0) + angle = (270-angle); + if (angle<0) { + angle = -angle-90; + if (angle<0) angle = 360-angle; + } + PointAngleType a; + a.first = points2D[j]; + a.second = angle; + angles.push_back(a); + } + + // Do nothing if less than 2 points --> n + if (points2D.size() < 3) { //continue; + continue; + } + + // Sort points2D according to polar angles + std::sort(angles.begin(), angles.end(), comparePointsWithAngle()); + for(unsigned int j=0; j toadd; + unsigned int index = 0; + double dmax = 5; + while (indexdmax) { + + MaskSlicePointType b; + b[0] = a[0]+(c[0]-a[0])/2.0; + b[1] = a[1]+(c[1]-a[1])/2.0; + + // Compute distance to trachea centroid + MaskSlicePointType m = centroids_trachea[1]; + double da = m.EuclideanDistanceTo(a); + double db = m.EuclideanDistanceTo(b); + //double dc = m.EuclideanDistanceTo(c); + + // Mean distance, find point on the line from trachea centroid + // to b + double alpha = (da+db)/2.0; + MaskSlicePointType v; + double n = sqrt( pow(b[0]-m[0], 2) + pow(b[1]-m[1], 2)); + v[0] = (b[0]-m[0])/n; + v[1] = (b[1]-m[1])/n; + MaskSlicePointType r; + r[0] = m[0]+alpha*v[0]; + r[1] = m[1]+alpha*v[1]; + points2D.insert(points2D.begin()+index+1, r); + } + else { + index++; + } + } + // DDV(points2D, points2D.size()); + + // Add some points to close the contour + // - H line towards Right + MaskSlicePointType p = points2D[0]; + p[0] = bounds[0][0]; + points2D.insert(points2D.begin(), p); + // - corner Right/Post + p = bounds[0]; + points2D.insert(points2D.begin(), p); + // - H line towards Left + p = points2D.back(); + p[0] = bounds[2][0]; + points2D.push_back(p); + // - corner Right/Post + p = bounds[2]; + points2D.push_back(p); + // Close contour with the first point + points2D.push_back(points2D[0]); + // DDV(points2D, points2D.size()); + + // Build 3D points from the 2D points + std::vector points3D; + clitk::PointsUtils::Convert2DListTo3DList(points2D, i, support, points3D); + for(unsigned int x=0; x mesh = Build3DMeshFrom2DContour(points3D); + append->AddInput(mesh); + } + + // DEBUG: write points3D + clitk::WriteListOfLandmarks(p3D, "vessels-centroids.txt"); + + // Build the final 3D mesh form the list 2D mesh + append->Update(); + vtkSmartPointer mesh = append->GetOutput(); + + // Debug, write the mesh + /* + vtkSmartPointer w = vtkSmartPointer::New(); + w->SetInput(mesh); + w->SetFileName("bidon.vtk"); + w->Write(); + */ + + // Compute a single binary 3D image from the list of contours + clitk::MeshToBinaryImageFilter::Pointer filter = + clitk::MeshToBinaryImageFilter::New(); + filter->SetMesh(mesh); + filter->SetLikeImage(support); + filter->Update(); + binarizedContour = filter->GetOutput(); + + // Crop + clitk::FindExtremaPointInAGivenDirection(binarizedContour, GetForegroundValue(), 2, true, p); + inf = p[2]; + DD(p); + clitk::FindExtremaPointInAGivenDirection(binarizedContour, GetForegroundValue(), 2, false, p); + sup = p[2]; + DD(p); + binarizedContour = clitk::CropImageAlongOneAxis(binarizedContour, 2, inf, sup, + false, GetBackgroundValue()); + // Update the AFDB + writeImage(binarizedContour, "seg/AntPostVesselsSeparation.mha"); + this->GetAFDB()->SetImageFilename("AntPostVesselsSeparation", "seg/AntPostVesselsSeparation.mha"); + this->WriteAFDB(); + return binarizedContour; + + /* + // Inverse binary mask if needed. We test a point that we know must be in FG. If it is not, inverse + ImagePointType p = p3D[2]; // This is the first centroid of the first slice + p[1] += 50; // 50 mm Post from this point must be kept + ImageIndexType index; + binarizedContour->TransformPhysicalPointToIndex(p, index); + bool isInside = (binarizedContour->GetPixel(index) != GetBackgroundValue()); + + // remove from support + typedef clitk::BooleanOperatorLabelImageFilter BoolFilterType; + typename BoolFilterType::Pointer boolFilter = BoolFilterType::New(); + boolFilter->InPlaceOn(); + boolFilter->SetInput1(m_Working_Support); + boolFilter->SetInput2(binarizedContour); + boolFilter->SetBackgroundValue1(GetBackgroundValue()); + boolFilter->SetBackgroundValue2(GetBackgroundValue()); + if (isInside) + boolFilter->SetOperationType(BoolFilterType::And); + else + boolFilter->SetOperationType(BoolFilterType::AndNot); + boolFilter->Update(); + m_Working_Support = boolFilter->GetOutput(); + */ + + // End + //StopCurrentStep(m_Working_Support); + //m_ListOfStations["2R"] = m_Working_Support; + //m_ListOfStations["2L"] = m_Working_Support; } //-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +typename clitk::ExtractLymphStationsFilter::MaskImagePointer +clitk::ExtractLymphStationsFilter:: +FindAntPostVessels2() +{ + // ----------------------------------------------------- + /* Rod says: "The anterior border, as with the Atlas – UM, is + posterior to the vessels (right subclavian vein, left + brachiocephalic vein, right brachiocephalic vein, left subclavian + artery, left common carotid artery and brachiocephalic trunk). + These vessels are not included in the nodal station. The anterior + border is drawn to the midpoint of the vessel and an imaginary + line joins the middle of these vessels. Between the vessels, + station 2 is in contact with station 3a." */ + + // Check if no already done + bool found = true; + MaskImagePointer binarizedContour; + try { + binarizedContour = this->GetAFDB()->template GetImage ("AntPostVesselsSeparation"); + } + catch(clitk::ExceptionObject e) { + found = false; + } + if (found) { + return binarizedContour; + } + + /* Here, we consider the vessels form a kind of anterior barrier. We + link all vessels centroids and remove what is post to it. + - select the list of structure + vessel1 = BrachioCephalicArtery + vessel2 = BrachioCephalicVein (warning several CCL, keep most at Right) + vessel3 = CommonCarotidArtery + vessel4 = SubclavianArtery + other = Thyroid + other = Aorta + - crop images as needed + - slice by slice, choose the CCL and extract centroids + - slice by slice, sort according to polar angle wrt Trachea centroid. + - slice by slice, link centroids and close contour + - remove outside this contour + - merge with support + */ + + // Read structures + std::map MapOfStructures; + typedef std::map::iterator MapIter; + MapOfStructures["BrachioCephalicArtery"] = this->GetAFDB()->template GetImage("BrachioCephalicArtery"); + MapOfStructures["BrachioCephalicVein"] = this->GetAFDB()->template GetImage("BrachioCephalicVein"); + MapOfStructures["CommonCarotidArteryLeft"] = this->GetAFDB()->template GetImage("LeftCommonCarotidArtery"); + MapOfStructures["CommonCarotidArteryRight"] = this->GetAFDB()->template GetImage("RightCommonCarotidArtery"); + MapOfStructures["SubclavianArteryLeft"] = this->GetAFDB()->template GetImage("LeftSubclavianArtery"); + MapOfStructures["SubclavianArteryRight"] = this->GetAFDB()->template GetImage("RightSubclavianArtery"); + MapOfStructures["Thyroid"] = this->GetAFDB()->template GetImage("Thyroid"); + MapOfStructures["Aorta"] = this->GetAFDB()->template GetImage("Aorta"); + MapOfStructures["Trachea"] = this->GetAFDB()->template GetImage("Trachea"); + + std::vector ListOfStructuresNames; + + // Create a temporay support + // From first slice of BrachioCephalicVein to end of 3A or end of 2RL + MaskImagePointer support = this->GetAFDB()->template GetImage("Support_Sup_Carina"); + MaskImagePointType p; + p[0] = p[1] = p[2] = 0.0; // to avoid warning + clitk::FindExtremaPointInAGivenDirection(MapOfStructures["BrachioCephalicVein"], + GetBackgroundValue(), 2, true, p); + double inf = p[2]; + clitk::FindExtremaPointInAGivenDirection(this->GetAFDB()->template GetImage("Support_S3A"), + GetBackgroundValue(), 2, false, p); + MaskImagePointType p2; + clitk::FindExtremaPointInAGivenDirection(this->GetAFDB()->template GetImage("Support_S2L"), + GetBackgroundValue(), 2, false, p2); + if (p2[2] > p[2]) p = p2; + + double sup = p[2]+support->GetSpacing()[2];//one slice more ? + support = clitk::CropImageAlongOneAxis(support, 2, inf, sup, + false, GetBackgroundValue()); + + // Resize all structures like support + for (MapIter it = MapOfStructures.begin(); it != MapOfStructures.end(); ++it) { + it->second = clitk::ResizeImageLike(it->second, support, GetBackgroundValue()); + } + + // Extract slices + typedef std::vector SlicesType; + std::map MapOfSlices; + for (MapIter it = MapOfStructures.begin(); it != MapOfStructures.end(); ++it) { + SlicesType s; + clitk::ExtractSlices(it->second, 2, s); + MapOfSlices[it->first] = s; + } + + unsigned int n= MapOfSlices["Trachea"].size(); + + // Get the boundaries of one slice + std::vector bounds; + ComputeImageBoundariesCoordinates(MapOfSlices["Trachea"][0], bounds); + + // LOOP ON SLICES + // For all slices, for all structures, find the centroid and build the contour + // List of 3D points (for debug) + std::vector p3D; + vtkSmartPointer append = vtkSmartPointer::New(); + for(unsigned int i=0; ifirst][i]; + s = clitk::Labelize(s, GetBackgroundValue(), true, 1); + } + + // Search centroids + std::vector points2D; + typedef std::vector CentroidsType; + std::map MapOfCentroids; + for (MapIter it = MapOfStructures.begin(); it != MapOfStructures.end(); ++it) { + std::string structure = it->first; + MaskSlicePointer & s = MapOfSlices[structure][i]; + CentroidsType c; + clitk::ComputeCentroids(s, GetBackgroundValue(), c); + MapOfCentroids[structure] = c; + } + + + // BrachioCephalicVein -> when it is separated into two CCL, we + // only consider the most at Right one + CentroidsType & c = MapOfCentroids["BrachioCephalicVein"]; + if (c.size() > 2) { + if (c[1][0] < c[2][0]) c.erase(c.begin()+2); + else c.erase(c.begin()+1); + } + + /* + // BrachioCephalicVein -> when SubclavianArtery has 2 CCL + // (BrachioCephalicArtery is divided) -> forget BrachioCephalicVein + if ((MapOfCentroids["BrachioCephalicArtery"].size() ==1) + && (MapOfCentroids["SubclavianArtery"].size() > 2)) { + MapOfCentroids["BrachioCephalicVein"].clear(); + } + */ + + // Add all 2D points + for (MapIter it = MapOfStructures.begin(); it != MapOfStructures.end(); ++it) { + std::string structure = it->first; + if (structure != "Trachea") { // do not add centroid of trachea + CentroidsType & c = MapOfCentroids[structure]; + for(unsigned int j=1; j centroids_trachea; + //ComputeCentroids(MapOfSlices["Trachea"][i], GetBackgroundValue(), centroids_trachea); + CentroidsType centroids_trachea = MapOfCentroids["Trachea"]; + typedef std::pair PointAngleType; + std::vector angles; + for(unsigned int j=0; j0) angle = atan(y/x); + if ((x<0) && (y>=0)) angle = atan(y/x)+M_PI; + if ((x<0) && (y<0)) angle = atan(y/x)-M_PI; + if (x==0) { + if (y>0) angle = M_PI/2.0; + if (y<0) angle = -M_PI/2.0; + if (y==0) angle = 0; + } + angle = clitk::rad2deg(angle); + // Angle is [-180;180] wrt the X axis. We change the X axis to + // be the vertical line, because we want to sort from Right to + // Left from Post to Ant. + if (angle>0) + angle = (270-angle); + if (angle<0) { + angle = -angle-90; + if (angle<0) angle = 360-angle; + } + PointAngleType a; + a.first = points2D[j]; + a.second = angle; + angles.push_back(a); + } + + // Do nothing if less than 2 points --> n + if (points2D.size() < 3) { //continue; + continue; + } + + // Sort points2D according to polar angles + std::sort(angles.begin(), angles.end(), comparePointsWithAngle()); + for(unsigned int j=0; j toadd; + unsigned int index = 0; + double dmax = 5; + while (indexdmax) { + + MaskSlicePointType b; + b[0] = a[0]+(c[0]-a[0])/2.0; + b[1] = a[1]+(c[1]-a[1])/2.0; + + // Compute distance to trachea centroid + MaskSlicePointType m = centroids_trachea[1]; + double da = m.EuclideanDistanceTo(a); + double db = m.EuclideanDistanceTo(b); + //double dc = m.EuclideanDistanceTo(c); + + // Mean distance, find point on the line from trachea centroid + // to b + double alpha = (da+db)/2.0; + MaskSlicePointType v; + double n = sqrt( pow(b[0]-m[0], 2) + pow(b[1]-m[1], 2)); + v[0] = (b[0]-m[0])/n; + v[1] = (b[1]-m[1])/n; + MaskSlicePointType r; + r[0] = m[0]+alpha*v[0]; + r[1] = m[1]+alpha*v[1]; + points2D.insert(points2D.begin()+index+1, r); + } + else { + index++; + } + } + // DDV(points2D, points2D.size()); + + // Add some points to close the contour + // - H line towards Right + MaskSlicePointType p = points2D[0]; + p[0] = bounds[0][0]; + points2D.insert(points2D.begin(), p); + // - corner Right/Post + p = bounds[0]; + points2D.insert(points2D.begin(), p); + // - H line towards Left + p = points2D.back(); + p[0] = bounds[2][0]; + points2D.push_back(p); + // - corner Right/Post + p = bounds[2]; + points2D.push_back(p); + // Close contour with the first point + points2D.push_back(points2D[0]); + // DDV(points2D, points2D.size()); + + // Build 3D points from the 2D points + std::vector points3D; + clitk::PointsUtils::Convert2DListTo3DList(points2D, i, support, points3D); + for(unsigned int x=0; x mesh = Build3DMeshFrom2DContour(points3D); + append->AddInput(mesh); + // if (i ==n-1) { // last slice + // clitk::PointsUtils::Convert2DListTo3DList(points2D, i+1, support, points3D); + // vtkSmartPointer mesh = Build3DMeshFrom2DContour(points3D); + // append->AddInput(mesh); + // } + } + + // DEBUG: write points3D + clitk::WriteListOfLandmarks(p3D, "vessels-centroids.txt"); + + // Build the final 3D mesh form the list 2D mesh + append->Update(); + vtkSmartPointer mesh = append->GetOutput(); + // Debug, write the mesh + /* + vtkSmartPointer w = vtkSmartPointer::New(); + w->SetInput(mesh); + w->SetFileName("bidon.vtk"); + w->Write(); + */ + + // Compute a single binary 3D image from the list of contours + clitk::MeshToBinaryImageFilter::Pointer filter = + clitk::MeshToBinaryImageFilter::New(); + filter->SetMesh(mesh); + filter->SetLikeImage(support); + filter->Update(); + binarizedContour = filter->GetOutput(); + + // Crop + clitk::FindExtremaPointInAGivenDirection(binarizedContour, + GetForegroundValue(), 2, true, p); + inf = p[2]; + clitk::FindExtremaPointInAGivenDirection(binarizedContour, + GetForegroundValue(), 2, false, p); + sup = p[2]+binarizedContour->GetSpacing()[2]; // extend to include the last slice + binarizedContour = clitk::CropImageAlongOneAxis(binarizedContour, 2, inf, sup, + false, GetBackgroundValue()); + + // Update the AFDB + writeImage(binarizedContour, "seg/AntPostVesselsSeparation.mha"); + this->GetAFDB()->SetImageFilename("AntPostVesselsSeparation", "seg/AntPostVesselsSeparation.mha"); + this->WriteAFDB(); + return binarizedContour; + + /* + // Inverse binary mask if needed. We test a point that we know must be in FG. If it is not, inverse + ImagePointType p = p3D[2]; // This is the first centroid of the first slice + p[1] += 50; // 50 mm Post from this point must be kept + ImageIndexType index; + binarizedContour->TransformPhysicalPointToIndex(p, index); + bool isInside = (binarizedContour->GetPixel(index) != GetBackgroundValue()); + + // remove from support + typedef clitk::BooleanOperatorLabelImageFilter BoolFilterType; + typename BoolFilterType::Pointer boolFilter = BoolFilterType::New(); + boolFilter->InPlaceOn(); + boolFilter->SetInput1(m_Working_Support); + boolFilter->SetInput2(binarizedContour); + boolFilter->SetBackgroundValue1(GetBackgroundValue()); + boolFilter->SetBackgroundValue2(GetBackgroundValue()); + if (isInside) + boolFilter->SetOperationType(BoolFilterType::And); + else + boolFilter->SetOperationType(BoolFilterType::AndNot); + boolFilter->Update(); + m_Working_Support = boolFilter->GetOutput(); + */ + + // End + //StopCurrentStep(m_Working_Support); + //m_ListOfStations["2R"] = m_Working_Support; + //m_ListOfStations["2L"] = m_Working_Support; +} +//-------------------------------------------------------------------- + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +WriteImageSupport(std::string support) +{ + writeImage(m_ListOfSupports[support], this->GetAFDBPath()+"/"+"seg/Support_"+support+".mha"); + this->GetAFDB()->SetImageFilename("Support_"+support, "seg/Support_"+support+".mha"); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +WriteImageStation(std::string station) +{ + writeImage(m_ListOfStations[station], GetAFDB()->GetPath()+"/seg/Station"+station+".mha"); + GetAFDB()->SetImageFilename("Station"+station, "seg/Station"+station+".mha"); + WriteAFDB(); +} +//-------------------------------------------------------------------- + + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ComputeOverlapWithRef(std::string station) +{ + if (GetComputeStation(station)) { + MaskImagePointer ref = this->GetAFDB()->template GetImage ("Station"+station+"_Ref"); + typedef clitk::LabelImageOverlapMeasureFilter FilterType; + typename FilterType::Pointer filter = FilterType::New(); + filter->SetInput(0, m_ListOfStations[station]); + filter->SetInput(1, ref); + filter->Update(); + } +} +//-------------------------------------------------------------------- + +//-------------------------------------------------------------------- +template +void +clitk::ExtractLymphStationsFilter:: +ReadSupportLimits(std::string filename) +{ + m_ListOfSupportLimits.clear(); + ifstream is; + openFileForReading(is, filename); + while (is) { + skipComment(is); + SupportLimitsType s; + s.Read(is); + if (is) m_ListOfSupportLimits.push_back(s); + } +} +//-------------------------------------------------------------------- #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX +