/*========================================================================= Program: vv http://www.creatis.insa-lyon.fr/rio/vv Authors belong to: - University of LYON http://www.universite-lyon.fr/ - Léon Bérard cancer center http://www.centreleonberard.fr - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the copyright notices for more information. It is distributed under dual licence - BSD See included LICENSE.txt file - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html ===========================================================================**/ #ifndef CLITKEXTRACTAIRWAYSTREEINFOSFILTER_TXX #define CLITKEXTRACTAIRWAYSTREEINFOSFILTER_TXX // clitk #include "clitkImageCommon.h" #include "clitkSetBackgroundImageFilter.h" #include "clitkSegmentationUtils.h" #include "clitkAutoCropFilter.h" #include "clitkExtractSliceFilter.h" // itk #include "itkBinaryThresholdImageFilter.h" #include "itkConnectedComponentImageFilter.h" #include "itkRelabelComponentImageFilter.h" #include "itkOtsuThresholdImageFilter.h" #include "itkBinaryThinningImageFilter3D.h" #include "itkImageIteratorWithIndex.h" //-------------------------------------------------------------------- template clitk::ExtractAirwaysTreeInfoFilter:: ExtractAirwaysTreeInfoFilter(): clitk::FilterBase(), clitk::FilterWithAnatomicalFeatureDatabaseManagement(), itk::ImageToImageFilter() { // Default global options this->SetNumberOfRequiredInputs(1); SetBackgroundValue(0); SetForegroundValue(1); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractAirwaysTreeInfoFilter:: SetInput(const ImageType * image) { this->SetNthInput(0, const_cast(image)); } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractAirwaysTreeInfoFilter:: GenerateOutputInformation() { Superclass::GenerateOutputInformation(); //this->GetOutput(0)->SetRequestedRegion(this->GetOutput(0)->GetLargestPossibleRegion()); // Get input pointer input = dynamic_cast(itk::ProcessObject::GetInput(0)); // Try to load the DB try { LoadAFDB(); } catch (clitk::ExceptionObject e) { // Do nothing if not found, it will be used anyway to write the result } } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractAirwaysTreeInfoFilter:: GenerateData() { // Crop just abov lungs ? // read skeleton if already exist bool foundSkeleton = true; try { skeleton = GetAFDB()->template GetImage ("skeleton"); } catch (clitk::ExceptionObject e) { DD("did not found skeleton"); foundSkeleton = false; } // Extract skeleton if (!foundSkeleton) { StartNewStep("Thinning filter (skeleton)"); typedef itk::BinaryThinningImageFilter3D ThinningFilterType; typename ThinningFilterType::Pointer thinningFilter = ThinningFilterType::New(); thinningFilter->SetInput(input); // input = trachea thinningFilter->Update(); skeleton = thinningFilter->GetOutput(); StopCurrentStep(skeleton); writeImage(skeleton, "skeleton.mhd"); } // Find first point for tracking StartNewStep("Find first point for tracking"); typedef itk::ImageRegionConstIteratorWithIndex IteratorType; IteratorType it(skeleton, skeleton->GetLargestPossibleRegion()); it.GoToReverseBegin(); while ((!it.IsAtEnd()) && (it.Get() == GetBackgroundValue())) { --it; } if (it.IsAtEnd()) { clitkExceptionMacro("first point in the trachea skeleton not found."); } typename ImageType::IndexType index = it.GetIndex(); DD(index); if (0) { // Initialize neighborhooditerator typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::SizeType radius; radius.Fill(1); NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion()); } // Find a label number different from BG and FG typename ImageType::PixelType label = GetForegroundValue()+1; while ((label == GetBackgroundValue()) || (label == GetForegroundValue())) { label++; } DD((int)label); /* Tracking ? Goal : find position of C, RUL, RML, RLL, LUL, LLL bronchus Carina : ok "easy", track, slice by slice until 2 path into different label -> follow at Right - -> follow at Left */ // NEW Track from the first point StartNewStep("Start tracking"); FullTreeNodeType n; n.index = index; skeleton->TransformIndexToPhysicalPoint(n.index, n.point); // à mettre dans TrackFromThisIndex2 mFullSkeletonTree.set_head(n); StructuralTreeNodeType sn; sn.index = index; skeleton->TransformIndexToPhysicalPoint(sn.index, sn.point); mStructuralSkeletonTree.set_head(sn); TrackFromThisIndex2(index, skeleton, label, mFullSkeletonTree.begin(), mStructuralSkeletonTree.begin()); StopCurrentStep(); // Reput FG instead of label in the skeleton image skeleton = clitk::SetBackground(skeleton, skeleton, label, GetForegroundValue(), true); // Debug typename StructuralTreeType::iterator sit = mStructuralSkeletonTree.begin(); while (sit != mStructuralSkeletonTree.end()) { DD(sit->point); ++sit; } // compute weight : n longueurs à chaque bifurfaction. // parcours de FullTreeNodeType, à partir de leaf, remonter de proche en proche la distance eucl. // par post order // Init typename FullTreeType::iterator fit = mFullSkeletonTree.begin(); while (fit != mFullSkeletonTree.end()) { fit->weight = 0.0; ++fit; } DD("compute weight"); typename FullTreeType::post_order_iterator pit = mFullSkeletonTree.begin_post(); while (pit != mFullSkeletonTree.end_post()) { //DD(pit->point); /* if (pit != mFullSkeletonTree.begin()) { typename FullTreeType::iterator parent = mFullSkeletonTree.parent(pit); double d = pit->point.EuclideanDistanceTo(parent->point); // DD(parent->weight); //DD(d); parent->weight += d; if (pit.number_of_children() > 1) { DD(pit.number_of_children()); DD(pit->point); DD(pit->weight); DD(parent->weight); DD(mFullSkeletonTree.child(pit, 0)->weight); DD(mFullSkeletonTree.child(pit, 1)->weight); } } */ double previous = pit->weight; for(uint i=0; ipoint.EuclideanDistanceTo(mFullSkeletonTree.child(pit, i)->point); // pit->weight = pit->weight + mFullSkeletonTree.child(pit, i)->weight + d; if (i==0) pit->weight = pit->weight + mFullSkeletonTree.child(pit, i)->weight + d; if (i>0) { DD(pit.number_of_children()); DD(pit->point); DD(pit->weight); DD(mFullSkeletonTree.child(pit, 0)->weight); DD(mFullSkeletonTree.child(pit, 1)->weight); pit->weight = std::max(pit->weight, previous+mFullSkeletonTree.child(pit, i)->weight + d); } } ++pit; } DD("end"); fit = mFullSkeletonTree.begin(); while (fit != mFullSkeletonTree.end()) { std::cout << "p = " << fit->point << " " << fit->weight << " child= " << fit.number_of_children() << " -> "; for(uint i=0; iweight; } std::cout << std::endl; ++fit; } /* Selection criteria ? - at least 2 child - from top to bottom - equilibr ? */ DD("========================================="); fit = mFullSkeletonTree.begin(); while (fit != mFullSkeletonTree.end()) { if (fit.number_of_children() > 1) { std::cout << "ppp = " << fit->point << " " << fit->weight << std::endl; for(uint i=1; iweight; double w2 = mFullSkeletonTree.child(fit, i)->weight; // if (w1 <= 0.1) break; // if (w2 <= 0.1) break; DD(w1);DD(w2); if (w1>w2) { double sw=w1; w1=w2; w2=sw; } DD(w1/w2); if (w1/w2 > 0.7) { DD("KEEP IT"); } } } ++fit; } if (0) { // Track from the first point StartNewStep("Start tracking OLD"); std::vector listOfBifurcations; m_SkeletonTree.set_head(index); TrackFromThisIndex(listOfBifurcations, skeleton, index, label, m_SkeletonTree.begin()); DD("end track"); // Convert index into physical point coordinates for(unsigned int i=0; iTransformIndexToPhysicalPoint(listOfBifurcations[i].index, listOfBifurcations[i].point); } // Search for the first slice that separate the bronchus // (carina). Labelize slice by slice, stop when the two points of // the skeleton ar not in the same connected component StartNewStep("Search for carina position"); typedef clitk::ExtractSliceFilter ExtractSliceFilterType; typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New(); extractSliceFilter->SetInput(input); extractSliceFilter->SetDirection(2); extractSliceFilter->Update(); typedef typename ExtractSliceFilterType::SliceType SliceType; std::vector mInputSlices; extractSliceFilter->GetOutputSlices(mInputSlices); DD(mInputSlices.size()); DD("REDO !!!!!!!!!!!!"); /** => chercher la bif qui a les plus important sous-arbres - iterate sur m_SkeletonTree depth first, remonter au parent, add value = distance NONNNNN m_SkeletonTree n'est pas la structure mais l'ensemble avec ts le skeleton TreeIterator itt = m_SkeletonTree.begin(); while (itt != m_SkeletonTree.end()) { itt->weight = 0.0; ++itt; } typename tree::post_order_iterator pit = m_SkeletonTree.begin_post(); while (pit != m_SkeletonTree.end_post()) { typename tree::iterator parent = m_SkeletonTree.parent(pit); double d = pit->point.EuclideanDistanceTo(parent); DD(parent.weight); DD(d); parent.weight += d; ++it; } itt = m_SkeletonTree.begin(); while (itt != m_SkeletonTree.end()) { DD(itt->weight); ++itt; } **/ // search for carina bool stop = false; int slice_index = listOfBifurcations[0].index[2]; // first slice from carina in skeleton int i=0; TreeIterator firstIter = m_SkeletonTree.child(listOfBifurcations[0].treeIter, 0); TreeIterator secondIter = m_SkeletonTree.child(listOfBifurcations[0].treeIter, 1); DD(firstIter.number_of_children()); DD(secondIter.number_of_children()); typename SliceType::IndexType in1; typename SliceType::IndexType in2; while (!stop) { // Labelize the current slice typename SliceType::Pointer temp = Labelize(mInputSlices[slice_index], GetBackgroundValue(), true, 0); // min component size=0 DD(*firstIter); DD(*secondIter); // Check the value of the two skeleton points; in1[0] = (*firstIter)[0]; in1[1] = (*firstIter)[1]; typename SliceType::PixelType v1 = temp->GetPixel(in1); in2[0] = (*secondIter)[0]; in2[1] = (*secondIter)[1]; typename SliceType::PixelType v2 = temp->GetPixel(in2); // Check the label value of the two points DD(slice_index); if (v1 != v2) { stop = true; // We found it ! } else { // Check error if (slice_index == (int)(mInputSlices.size()-1)) { clitkExceptionMacro("Error while searching for carina, the two skeleton points are always in the same CC ... ???"); } // Iterate i++; --slice_index; ++firstIter; ++secondIter; } } ImageIndexType carina_index; // middle position in X/Y carina_index[0] = lrint(in2[0] + in1[0])/2.0; carina_index[1] = lrint(in2[1] + in1[1])/2.0; carina_index[2] = slice_index; // Get physical coordinates ImagePointType carina_position; skeleton->TransformIndexToPhysicalPoint(carina_index, carina_position); // Set and save Carina position if (GetVerboseStepFlag()) { std::cout << "\t Found carina at " << carina_position << " mm" << std::endl; } GetAFDB()->SetPoint3D("carina", carina_position); // Write bifurcation (debug) for(uint i=0; iSetPoint3D("Bif"+toString(i), listOfBifurcations[i].point); } } // Set the output (skeleton); this->GraftOutput(skeleton); // not SetNthOutput } //-------------------------------------------------------------------- //-------------------------------------------------------------------- /** Start from the pixel "index" in the image "skeleton" and track the path from neighbor pixels. Put the tracked path in the tree at the currentNode position. Label is used to mark the FG pixel as already visited. Progress recursively when several neighbors are found. **/ template void clitk::ExtractAirwaysTreeInfoFilter:: TrackFromThisIndex2(ImageIndexType index, ImagePointer skeleton, ImagePixelType label, typename FullTreeType::iterator currentNode, typename StructuralTreeType::iterator currentSNode) { // Create NeighborhoodIterator typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::SizeType radius; radius.Fill(1); NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion()); // Track std::vector listOfTrackedPoint; bool stop = false; while (!stop) { nit.SetLocation(index); nit.SetCenterPixel(label); listOfTrackedPoint.clear(); for(unsigned int i=0; iTransformIndexToPhysicalPoint(n.index, n.point); currentNode = mFullSkeletonTree.append_child(currentNode, n); skeleton->SetPixel(n.index, label); // change label in skeleton image } else { if (listOfTrackedPoint.size() >= 2) { for(uint i=0; iTransformIndexToPhysicalPoint(n.index, n.point); typename FullTreeType::iterator node = mFullSkeletonTree.append_child(currentNode, n); StructuralTreeNodeType sn; sn.index = listOfTrackedPoint[i]; skeleton->TransformIndexToPhysicalPoint(sn.index, sn.point); typename StructuralTreeType::iterator snode = mStructuralSkeletonTree.append_child(currentSNode, sn); TrackFromThisIndex2(listOfTrackedPoint[i], skeleton, label, node, snode); } } stop = true; // this it the end of the tracking } // end else } // end while stop } //-------------------------------------------------------------------- //-------------------------------------------------------------------- template void clitk::ExtractAirwaysTreeInfoFilter:: TrackFromThisIndex(std::vector & listOfBifurcations, ImagePointer skeleton, ImageIndexType index, ImagePixelType label, TreeIterator currentNode) { // Create NeighborhoodIterator typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::SizeType radius; radius.Fill(1); NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion()); // Track std::vector listOfTrackedPoint; bool stop = false; while (!stop) { nit.SetLocation(index); nit.SetCenterPixel(label); listOfTrackedPoint.clear(); for(unsigned int i=0; iSetPixel(index, label); // change label in skeleton image } else { if (listOfTrackedPoint.size() == 2) { // m_SkeletonTree->Add(listOfTrackedPoint[0], index); // the parent is 'index' // m_SkeletonTree->Add(listOfTrackedPoint[1], index); // the parent is 'index' DD("BifurcationType"); DD(listOfTrackedPoint[0]); DD(listOfTrackedPoint[1]); BifurcationType bif(index, label, label+1, label+2); bif.treeIter = currentNode; listOfBifurcations.push_back(bif); TreeIterator firstNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]); TreeIterator secondNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[1]); TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1, firstNode); TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2, secondNode); } else { //DD(listOfTrackedPoint.size()); if (listOfTrackedPoint.size() > 2) { //clitkExceptionMacro("error while tracking trachea bifurcation. Too much bifurcation points ... ?"); stop = true; // this it the end of the tracking } // Else this it the end of the tracking } stop = true; } } } //-------------------------------------------------------------------- /*TrackFromThisIndex(std::vector & listOfBifurcations, ImagePointer skeleton, ImageIndexType index, ImagePixelType label) { DD("TrackFromThisIndex"); DD(index); DD((int)label); // Create NeighborhoodIterator typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::SizeType radius; radius.Fill(1); NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion()); // Track std::vector listOfTrackedPoint; bool stop = false; while (!stop) { nit.SetLocation(index); // DD((int)nit.GetCenterPixel()); nit.SetCenterPixel(label); listOfTrackedPoint.clear(); for(unsigned int i=0; i 2) { std::cerr << "too much bifurcation points ... ?" << std::endl; exit(0); } // Else this it the end of the tracking } stop = true; } } } */ //-------------------------------------------------------------------- #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX