1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
5 - University of LYON http://www.universite-lyon.fr/
6 - Léon Bérard cancer center http://oncora1.lyon.fnclcc.fr
7 - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the copyright notices for more information.
13 It is distributed under dual licence
15 - BSD See included LICENSE.txt file
16 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ======================================================================-====*/
19 #ifndef CLITKEXTRACTAIRWAYSTREEINFOSFILTER_TXX
20 #define CLITKEXTRACTAIRWAYSTREEINFOSFILTER_TXX
23 #include "clitkImageCommon.h"
24 #include "clitkSetBackgroundImageFilter.h"
25 #include "clitkSegmentationUtils.h"
26 #include "clitkAutoCropFilter.h"
27 #include "clitkExtractSliceFilter.h"
30 #include "itkBinaryThresholdImageFilter.h"
31 #include "itkConnectedComponentImageFilter.h"
32 #include "itkRelabelComponentImageFilter.h"
33 #include "itkOtsuThresholdImageFilter.h"
34 #include "itkBinaryThinningImageFilter3D.h"
35 #include "itkImageIteratorWithIndex.h"
38 //--------------------------------------------------------------------
39 template <class ImageType>
40 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
41 ExtractAirwaysTreeInfoFilter():
43 clitk::FilterWithAnatomicalFeatureDatabaseManagement(),
44 itk::ImageToImageFilter<ImageType, ImageType>()
46 // Default global options
47 this->SetNumberOfRequiredInputs(1);
48 SetBackgroundValue(0);
49 SetForegroundValue(1);
51 //--------------------------------------------------------------------
54 //--------------------------------------------------------------------
55 template <class ImageType>
57 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
58 SetInput(const ImageType * image)
60 this->SetNthInput(0, const_cast<ImageType *>(image));
62 //--------------------------------------------------------------------
65 //--------------------------------------------------------------------
66 template <class ImageType>
67 template<class ArgsInfoType>
69 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
70 SetArgsInfo(ArgsInfoType mArgsInfo)
72 SetVerboseOption_GGO(mArgsInfo);
73 SetVerboseStep_GGO(mArgsInfo);
74 SetWriteStep_GGO(mArgsInfo);
75 SetVerboseWarningOff_GGO(mArgsInfo);
76 SetAFDBFilename_GGO(mArgsInfo);
78 //--------------------------------------------------------------------
81 //--------------------------------------------------------------------
82 template <class ImageType>
84 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
85 GenerateOutputInformation()
87 Superclass::GenerateOutputInformation();
88 //this->GetOutput(0)->SetRequestedRegion(this->GetOutput(0)->GetLargestPossibleRegion());
91 input = dynamic_cast<const ImageType*>(itk::ProcessObject::GetInput(0));
96 } catch (clitk::ExceptionObject e) {
97 // Do nothing if not found, it will be used anyway to write the result
102 //--------------------------------------------------------------------
105 //--------------------------------------------------------------------
106 template <class ImageType>
108 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
112 // Crop just abov lungs ?
114 // read skeleton if already exist
115 bool foundSkeleton = true;
117 skeleton = GetAFDB()->template GetImage <ImageType>("skeleton");
119 catch (clitk::ExceptionObject e) {
120 DD("did not found skeleton");
121 foundSkeleton = false;
125 if (!foundSkeleton) {
126 StartNewStep("Thinning filter (skeleton)");
127 typedef itk::BinaryThinningImageFilter3D<ImageType, ImageType> ThinningFilterType;
128 typename ThinningFilterType::Pointer thinningFilter = ThinningFilterType::New();
129 thinningFilter->SetInput(input); // input = trachea
130 thinningFilter->Update();
131 skeleton = thinningFilter->GetOutput();
132 StopCurrentStep<ImageType>(skeleton);
133 writeImage<ImageType>(skeleton, "skeleton.mhd");
136 // Find first point for tracking
137 StartNewStep("Find first point for tracking");
138 typedef itk::ImageRegionConstIteratorWithIndex<ImageType> IteratorType;
139 IteratorType it(skeleton, skeleton->GetLargestPossibleRegion());
140 it.GoToReverseBegin();
141 while ((!it.IsAtEnd()) && (it.Get() == GetBackgroundValue())) {
145 clitkExceptionMacro("first point in the trachea skeleton not found.");
147 typename ImageType::IndexType index = it.GetIndex();
151 // Initialize neighborhooditerator
152 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
153 typename NeighborhoodIteratorType::SizeType radius;
155 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
158 // Find a label number different from BG and FG
159 typename ImageType::PixelType label = GetForegroundValue()+1;
160 while ((label == GetBackgroundValue()) || (label == GetForegroundValue())) { label++; }
165 Goal : find position of C, RUL, RML, RLL, LUL, LLL bronchus
166 Carina : ok "easy", track, slice by slice until 2 path into different label
172 // NEW Track from the first point
173 StartNewStep("Start tracking");
176 skeleton->TransformIndexToPhysicalPoint(n.index, n.point); // à mettre dans TrackFromThisIndex2
177 mFullSkeletonTree.set_head(n);
178 StructuralTreeNodeType sn;
180 skeleton->TransformIndexToPhysicalPoint(sn.index, sn.point);
181 mStructuralSkeletonTree.set_head(sn);
182 TrackFromThisIndex2(index, skeleton, label, mFullSkeletonTree.begin(), mStructuralSkeletonTree.begin());
185 // Reput FG instead of label in the skeleton image
186 skeleton = clitk::SetBackground<ImageType, ImageType>(skeleton, skeleton, label, GetForegroundValue());
189 typename StructuralTreeType::iterator sit = mStructuralSkeletonTree.begin();
190 while (sit != mStructuralSkeletonTree.end()) {
195 // compute weight : n longueurs à chaque bifurfaction.
196 // parcours de FullTreeNodeType, à partir de leaf, remonter de proche en proche la distance eucl.
200 typename FullTreeType::iterator fit = mFullSkeletonTree.begin();
201 while (fit != mFullSkeletonTree.end()) {
206 DD("compute weight");
207 typename FullTreeType::post_order_iterator pit = mFullSkeletonTree.begin_post();
208 while (pit != mFullSkeletonTree.end_post()) {
211 if (pit != mFullSkeletonTree.begin()) {
212 typename FullTreeType::iterator parent = mFullSkeletonTree.parent(pit);
213 double d = pit->point.EuclideanDistanceTo(parent->point);
214 // DD(parent->weight);
217 if (pit.number_of_children() > 1) {
218 DD(pit.number_of_children());
222 DD(mFullSkeletonTree.child(pit, 0)->weight);
223 DD(mFullSkeletonTree.child(pit, 1)->weight);
227 double previous = pit->weight;
228 for(uint i=0; i<pit.number_of_children(); i++) {
229 double d = pit->point.EuclideanDistanceTo(mFullSkeletonTree.child(pit, i)->point);
230 // pit->weight = pit->weight + mFullSkeletonTree.child(pit, i)->weight + d;
232 pit->weight = pit->weight + mFullSkeletonTree.child(pit, i)->weight + d;
234 DD(pit.number_of_children());
237 DD(mFullSkeletonTree.child(pit, 0)->weight);
238 DD(mFullSkeletonTree.child(pit, 1)->weight);
239 pit->weight = std::max(pit->weight, previous+mFullSkeletonTree.child(pit, i)->weight + d);
246 fit = mFullSkeletonTree.begin();
247 while (fit != mFullSkeletonTree.end()) {
248 std::cout << "p = " << fit->point
249 << " " << fit->weight
250 << " child= " << fit.number_of_children()
252 for(uint i=0; i<fit.number_of_children(); i++) {
253 std::cout << " " << mFullSkeletonTree.child(fit, i)->weight;
255 std::cout << std::endl;
259 /* Selection criteria ?
264 DD("=========================================");
265 fit = mFullSkeletonTree.begin();
266 while (fit != mFullSkeletonTree.end()) {
267 if (fit.number_of_children() > 1) {
268 std::cout << "ppp = " << fit->point
269 << " " << fit->weight << std::endl;
270 for(uint i=1; i<fit.number_of_children(); i++) {
271 double w1 = mFullSkeletonTree.child(fit, i-1)->weight;
272 double w2 = mFullSkeletonTree.child(fit, i)->weight;
273 // if (w1 <= 0.1) break;
274 // if (w2 <= 0.1) break;
291 // Track from the first point
292 StartNewStep("Start tracking OLD");
293 std::vector<BifurcationType> listOfBifurcations;
294 m_SkeletonTree.set_head(index);
295 TrackFromThisIndex(listOfBifurcations, skeleton, index, label, m_SkeletonTree.begin());
298 // Convert index into physical point coordinates
299 for(unsigned int i=0; i<listOfBifurcations.size(); i++) {
300 skeleton->TransformIndexToPhysicalPoint(listOfBifurcations[i].index,
301 listOfBifurcations[i].point);
304 // Search for the first slice that separate the bronchus
305 // (carina). Labelize slice by slice, stop when the two points of
306 // the skeleton ar not in the same connected component
307 StartNewStep("Search for carina position");
308 typedef clitk::ExtractSliceFilter<ImageType> ExtractSliceFilterType;
309 typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
310 extractSliceFilter->SetInput(input);
311 extractSliceFilter->SetDirection(2);
312 extractSliceFilter->Update();
313 typedef typename ExtractSliceFilterType::SliceType SliceType;
314 std::vector<typename SliceType::Pointer> mInputSlices;
315 extractSliceFilter->GetOutputSlices(mInputSlices);
316 DD(mInputSlices.size());
319 DD("REDO !!!!!!!!!!!!");
321 => chercher la bif qui a les plus important sous-arbres
322 - iterate sur m_SkeletonTree depth first, remonter au parent, add value = distance
324 NONNNNN m_SkeletonTree n'est pas la structure mais l'ensemble avec ts le skeleton
326 TreeIterator itt = m_SkeletonTree.begin();
327 while (itt != m_SkeletonTree.end()) {
332 typename tree<NodeType>::post_order_iterator pit = m_SkeletonTree.begin_post();
333 while (pit != m_SkeletonTree.end_post()) {
334 typename tree<NodeType>::iterator parent = m_SkeletonTree.parent(pit);
335 double d = pit->point.EuclideanDistanceTo(parent);
342 itt = m_SkeletonTree.begin();
343 while (itt != m_SkeletonTree.end()) {
352 int slice_index = listOfBifurcations[0].index[2]; // first slice from carina in skeleton
354 TreeIterator firstIter = m_SkeletonTree.child(listOfBifurcations[0].treeIter, 0);
355 TreeIterator secondIter = m_SkeletonTree.child(listOfBifurcations[0].treeIter, 1);
356 DD(firstIter.number_of_children());
357 DD(secondIter.number_of_children());
358 typename SliceType::IndexType in1;
359 typename SliceType::IndexType in2;
361 // Labelize the current slice
362 typename SliceType::Pointer temp = Labelize<SliceType>(mInputSlices[slice_index],
363 GetBackgroundValue(),
365 0); // min component size=0
368 // Check the value of the two skeleton points;
369 in1[0] = (*firstIter)[0];
370 in1[1] = (*firstIter)[1];
371 typename SliceType::PixelType v1 = temp->GetPixel(in1);
372 in2[0] = (*secondIter)[0];
373 in2[1] = (*secondIter)[1];
374 typename SliceType::PixelType v2 = temp->GetPixel(in2);
376 // Check the label value of the two points
379 stop = true; // We found it !
383 if (slice_index == (int)(mInputSlices.size()-1)) {
384 clitkExceptionMacro("Error while searching for carina, the two skeleton points are always in the same CC ... ???");
393 ImageIndexType carina_index; // middle position in X/Y
394 carina_index[0] = lrint(in2[0] + in1[0])/2.0;
395 carina_index[1] = lrint(in2[1] + in1[1])/2.0;
396 carina_index[2] = slice_index;
397 // Get physical coordinates
398 ImagePointType carina_position;
399 skeleton->TransformIndexToPhysicalPoint(carina_index,
402 // Set and save Carina position
403 if (GetVerboseStep()) {
404 std::cout << "\t Found carina at " << carina_position << " mm" << std::endl;
406 GetAFDB()->SetPoint3D("carina", carina_position);
408 // Write bifurcation (debug)
409 for(uint i=0; i<listOfBifurcations.size(); i++) {
410 GetAFDB()->SetPoint3D("Bif"+toString(i), listOfBifurcations[i].point);
414 // Set the output (skeleton);
415 this->GraftOutput(skeleton); // not SetNthOutput
417 //--------------------------------------------------------------------
420 //--------------------------------------------------------------------
422 Start from the pixel "index" in the image "skeleton" and track the
423 path from neighbor pixels. Put the tracked path in the tree at the
424 currentNode position. Label is used to mark the FG pixel as already
425 visited. Progress recursively when several neighbors are found.
427 template <class ImageType>
429 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
430 TrackFromThisIndex2(ImageIndexType index, ImagePointer skeleton,
431 ImagePixelType label,
432 typename FullTreeType::iterator currentNode,
433 typename StructuralTreeType::iterator currentSNode)
435 // Create NeighborhoodIterator
436 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
437 typename NeighborhoodIteratorType::SizeType radius;
439 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
442 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
445 nit.SetLocation(index);
446 nit.SetCenterPixel(label);
447 listOfTrackedPoint.clear();
448 for(unsigned int i=0; i<nit.Size(); i++) {
449 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
450 if (nit.GetPixel(i) == GetForegroundValue()) {
451 // if pixel value is FG, we keep this point
452 listOfTrackedPoint.push_back(nit.GetIndex(i));
456 // If only neighbor is found, we keep the point and continue
457 if (listOfTrackedPoint.size() == 1) {
459 index = n.index = listOfTrackedPoint[0];
460 skeleton->TransformIndexToPhysicalPoint(n.index, n.point);
461 currentNode = mFullSkeletonTree.append_child(currentNode, n);
462 skeleton->SetPixel(n.index, label); // change label in skeleton image
465 if (listOfTrackedPoint.size() >= 2) {
466 for(uint i=0; i<listOfTrackedPoint.size(); i++) {
468 n.index = listOfTrackedPoint[i];
469 skeleton->TransformIndexToPhysicalPoint(n.index, n.point);
470 typename FullTreeType::iterator node = mFullSkeletonTree.append_child(currentNode, n);
471 StructuralTreeNodeType sn;
472 sn.index = listOfTrackedPoint[i];
473 skeleton->TransformIndexToPhysicalPoint(sn.index, sn.point);
474 typename StructuralTreeType::iterator snode = mStructuralSkeletonTree.append_child(currentSNode, sn);
475 TrackFromThisIndex2(listOfTrackedPoint[i], skeleton, label, node, snode);
478 stop = true; // this it the end of the tracking
482 //--------------------------------------------------------------------
484 //--------------------------------------------------------------------
485 template <class ImageType>
487 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
488 TrackFromThisIndex(std::vector<BifurcationType> & listOfBifurcations,
489 ImagePointer skeleton,
490 ImageIndexType index,
491 ImagePixelType label,
492 TreeIterator currentNode)
494 // Create NeighborhoodIterator
495 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
496 typename NeighborhoodIteratorType::SizeType radius;
498 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
501 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
504 nit.SetLocation(index);
505 nit.SetCenterPixel(label);
506 listOfTrackedPoint.clear();
507 for(unsigned int i=0; i<nit.Size(); i++) {
508 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
509 if (nit.GetPixel(i) == GetForegroundValue()) { // if this is foreground, we continue the tracking
510 listOfTrackedPoint.push_back(nit.GetIndex(i));
514 if (listOfTrackedPoint.size() == 1) {
515 // Add this point to the current path
516 currentNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]);
517 index = listOfTrackedPoint[0];
518 skeleton->SetPixel(index, label); // change label in skeleton image
521 if (listOfTrackedPoint.size() == 2) {
522 // m_SkeletonTree->Add(listOfTrackedPoint[0], index); // the parent is 'index'
523 // m_SkeletonTree->Add(listOfTrackedPoint[1], index); // the parent is 'index'
524 DD("BifurcationType");
525 DD(listOfTrackedPoint[0]);
526 DD(listOfTrackedPoint[1]);
527 BifurcationType bif(index, label, label+1, label+2);
528 bif.treeIter = currentNode;
529 listOfBifurcations.push_back(bif);
530 TreeIterator firstNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]);
531 TreeIterator secondNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[1]);
532 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1, firstNode);
533 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2, secondNode);
536 //DD(listOfTrackedPoint.size());
537 if (listOfTrackedPoint.size() > 2) {
538 //clitkExceptionMacro("error while tracking trachea bifurcation. Too much bifurcation points ... ?");
539 stop = true; // this it the end of the tracking
541 // Else this it the end of the tracking
547 //--------------------------------------------------------------------
549 /*TrackFromThisIndex(std::vector<BifurcationType> & listOfBifurcations,
550 ImagePointer skeleton,
551 ImageIndexType index,
552 ImagePixelType label)
554 DD("TrackFromThisIndex");
557 // Create NeighborhoodIterator
558 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
559 typename NeighborhoodIteratorType::SizeType radius;
561 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
564 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
567 nit.SetLocation(index);
568 // DD((int)nit.GetCenterPixel());
569 nit.SetCenterPixel(label);
570 listOfTrackedPoint.clear();
571 for(unsigned int i=0; i<nit.Size(); i++) {
572 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
573 // DD(nit.GetIndex(i));
574 if (nit.GetPixel(i) == GetForegroundValue()) { // if this is foreground, we continue the tracking
575 // DD(nit.GetIndex(i));
576 listOfTrackedPoint.push_back(nit.GetIndex(i));
580 // DD(listOfTrackedPoint.size());
581 if (listOfTrackedPoint.size() == 1) {
582 index = listOfTrackedPoint[0];
585 if (listOfTrackedPoint.size() == 2) {
586 BifurcationType bif(index, label, label+1, label+2);
587 listOfBifurcations.push_back(bif);
588 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1);
589 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2);
592 if (listOfTrackedPoint.size() > 2) {
593 std::cerr << "too much bifurcation points ... ?" << std::endl;
596 // Else this it the end of the tracking
603 //--------------------------------------------------------------------
606 #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX