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>::
111 StartNewStep("Thinning filter (skeleton)");
113 typedef itk::BinaryThinningImageFilter3D<ImageType, ImageType> ThinningFilterType;
114 typename ThinningFilterType::Pointer thinningFilter = ThinningFilterType::New();
115 thinningFilter->SetInput(input); // input = trachea
116 thinningFilter->Update();
117 skeleton = thinningFilter->GetOutput();
118 StopCurrentStep<ImageType>(skeleton);
120 // Find first point for tracking
121 StartNewStep("Find first point for tracking");
122 typedef itk::ImageRegionConstIteratorWithIndex<ImageType> IteratorType;
123 IteratorType it(skeleton, skeleton->GetLargestPossibleRegion());
124 it.GoToReverseBegin();
125 while ((!it.IsAtEnd()) && (it.Get() == GetBackgroundValue())) {
129 clitkExceptionMacro("first point in the trachea skeleton not found.");
131 typename ImageType::IndexType index = it.GetIndex();
134 // Initialize neighborhooditerator
135 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
136 typename NeighborhoodIteratorType::SizeType radius;
138 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
140 // Find first label number (must be different from BG and FG)
141 typename ImageType::PixelType label = GetForegroundValue()+1;
142 while ((label == GetBackgroundValue()) || (label == GetForegroundValue())) { label++; }
147 Goal : find position of C, RUL, RML, RLL, LUL, LLL bronchus
148 Carina : ok "easy", track, slice by slice until 2 path into different label
155 // Track from the first point
156 StartNewStep("Start tracking");
157 std::vector<BifurcationType> listOfBifurcations;
158 m_SkeletonTree.set_head(index);
159 TrackFromThisIndex(listOfBifurcations, skeleton, index, label, m_SkeletonTree.begin());
162 // Convert index into physical point coordinates
163 for(unsigned int i=0; i<listOfBifurcations.size(); i++) {
164 skeleton->TransformIndexToPhysicalPoint(listOfBifurcations[i].index,
165 listOfBifurcations[i].point);
168 // Search for the first slice that separate the bronchus
169 // (carina). Labelize slice by slice, stop when the two points of
170 // the skeleton ar not in the same connected component
171 StartNewStep("Search for carina position");
172 typedef clitk::ExtractSliceFilter<ImageType> ExtractSliceFilterType;
173 typename ExtractSliceFilterType::Pointer extractSliceFilter = ExtractSliceFilterType::New();
174 extractSliceFilter->SetInput(input);
175 extractSliceFilter->SetDirection(2);
176 extractSliceFilter->Update();
177 typedef typename ExtractSliceFilterType::SliceType SliceType;
178 std::vector<typename SliceType::Pointer> mInputSlices;
179 extractSliceFilter->GetOutputSlices(mInputSlices);
180 DD(mInputSlices.size());
183 DD("REDO !!!!!!!!!!!!");
185 => chercher la bif qui a les plus important sous-arbres
189 int slice_index = listOfBifurcations[0].index[2]; // first slice from carina in skeleton
191 TreeIterator firstIter = m_SkeletonTree.child(listOfBifurcations[0].treeIter, 0);
192 TreeIterator secondIter = m_SkeletonTree.child(listOfBifurcations[0].treeIter, 1);
193 DD(firstIter.number_of_children());
194 DD(secondIter.number_of_children());
195 typename SliceType::IndexType in1;
196 typename SliceType::IndexType in2;
198 // Labelize the current slice
199 typename SliceType::Pointer temp = Labelize<SliceType>(mInputSlices[slice_index],
200 GetBackgroundValue(),
202 0); // min component size=0
205 // Check the value of the two skeleton points;
206 in1[0] = (*firstIter)[0];
207 in1[1] = (*firstIter)[1];
208 typename SliceType::PixelType v1 = temp->GetPixel(in1);
209 in2[0] = (*secondIter)[0];
210 in2[1] = (*secondIter)[1];
211 typename SliceType::PixelType v2 = temp->GetPixel(in2);
213 // Check the label value of the two points
216 stop = true; // We found it !
220 if (slice_index == (int)(mInputSlices.size()-1)) {
221 clitkExceptionMacro("Error while searching for carina, the two skeleton points are always in the same CC ... ???");
230 ImageIndexType carina_index; // middle position in X/Y
231 carina_index[0] = lrint(in2[0] + in1[0])/2.0;
232 carina_index[1] = lrint(in2[1] + in1[1])/2.0;
233 carina_index[2] = slice_index;
234 // Get physical coordinates
235 ImagePointType carina_position;
236 skeleton->TransformIndexToPhysicalPoint(carina_index,
239 // Set and save Carina position
240 if (GetVerboseStep()) {
241 std::cout << "\t Found carina at " << carina_position << " mm" << std::endl;
243 GetAFDB()->SetPoint3D("Carina", carina_position);
245 // Write bifurcation (debug)
246 for(uint i=0; i<listOfBifurcations.size(); i++) {
247 GetAFDB()->SetPoint3D("Bif"+toString(i), listOfBifurcations[i].point);
250 // Set the output (skeleton);
251 this->GraftOutput(skeleton); // not SetNthOutput
253 //--------------------------------------------------------------------
256 //--------------------------------------------------------------------
257 template <class ImageType>
259 clitk::ExtractAirwaysTreeInfoFilter<ImageType>::
260 TrackFromThisIndex(std::vector<BifurcationType> & listOfBifurcations,
261 ImagePointer skeleton,
262 ImageIndexType index,
263 ImagePixelType label,
264 TreeIterator currentNode)
266 // Create NeighborhoodIterator
267 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
268 typename NeighborhoodIteratorType::SizeType radius;
270 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
273 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
276 nit.SetLocation(index);
277 nit.SetCenterPixel(label);
278 listOfTrackedPoint.clear();
279 for(unsigned int i=0; i<nit.Size(); i++) {
280 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
281 if (nit.GetPixel(i) == GetForegroundValue()) { // if this is foreground, we continue the tracking
282 listOfTrackedPoint.push_back(nit.GetIndex(i));
286 if (listOfTrackedPoint.size() == 1) {
287 // Add this point to the current path
288 currentNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]);
289 index = listOfTrackedPoint[0];
290 skeleton->SetPixel(index, label); // change label in skeleton image
293 if (listOfTrackedPoint.size() == 2) {
294 // m_SkeletonTree->Add(listOfTrackedPoint[0], index); // the parent is 'index'
295 // m_SkeletonTree->Add(listOfTrackedPoint[1], index); // the parent is 'index'
296 DD("BifurcationType");
297 DD(listOfTrackedPoint[0]);
298 DD(listOfTrackedPoint[1]);
299 BifurcationType bif(index, label, label+1, label+2);
300 bif.treeIter = currentNode;
301 listOfBifurcations.push_back(bif);
302 TreeIterator firstNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[0]);
303 TreeIterator secondNode = m_SkeletonTree.append_child(currentNode, listOfTrackedPoint[1]);
304 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1, firstNode);
305 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2, secondNode);
308 //DD(listOfTrackedPoint.size());
309 if (listOfTrackedPoint.size() > 2) {
310 //clitkExceptionMacro("error while tracking trachea bifurcation. Too much bifurcation points ... ?");
311 stop = true; // this it the end of the tracking
313 // Else this it the end of the tracking
319 //--------------------------------------------------------------------
321 /*TrackFromThisIndex(std::vector<BifurcationType> & listOfBifurcations,
322 ImagePointer skeleton,
323 ImageIndexType index,
324 ImagePixelType label)
326 DD("TrackFromThisIndex");
329 // Create NeighborhoodIterator
330 typedef itk::NeighborhoodIterator<ImageType> NeighborhoodIteratorType;
331 typename NeighborhoodIteratorType::SizeType radius;
333 NeighborhoodIteratorType nit(radius, skeleton, skeleton->GetLargestPossibleRegion());
336 std::vector<typename NeighborhoodIteratorType::IndexType> listOfTrackedPoint;
339 nit.SetLocation(index);
340 // DD((int)nit.GetCenterPixel());
341 nit.SetCenterPixel(label);
342 listOfTrackedPoint.clear();
343 for(unsigned int i=0; i<nit.Size(); i++) {
344 if (i != nit.GetCenterNeighborhoodIndex ()) { // Do not observe the current point
345 // DD(nit.GetIndex(i));
346 if (nit.GetPixel(i) == GetForegroundValue()) { // if this is foreground, we continue the tracking
347 // DD(nit.GetIndex(i));
348 listOfTrackedPoint.push_back(nit.GetIndex(i));
352 // DD(listOfTrackedPoint.size());
353 if (listOfTrackedPoint.size() == 1) {
354 index = listOfTrackedPoint[0];
357 if (listOfTrackedPoint.size() == 2) {
358 BifurcationType bif(index, label, label+1, label+2);
359 listOfBifurcations.push_back(bif);
360 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[0], label+1);
361 TrackFromThisIndex(listOfBifurcations, skeleton, listOfTrackedPoint[1], label+2);
364 if (listOfTrackedPoint.size() > 2) {
365 std::cerr << "too much bifurcation points ... ?" << std::endl;
368 // Else this it the end of the tracking
375 //--------------------------------------------------------------------
378 #endif //#define CLITKBOOLEANOPERATORLABELIMAGEFILTER_TXX