Orfeo Toolbox  3.16
itkCannyEdgeDetectionImageFilter.txx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkCannyEdgeDetectionImageFilter.txx,v $
5  Language: C++
6  Date: $Date: 2009-08-17 12:01:33 $
7  Version: $Revision: 1.56 $
8 
9  Copyright (c) Insight Software Consortium. All rights reserved.
10  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
11 
12  This software is distributed WITHOUT ANY WARRANTY; without even
13  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14  PURPOSE. See the above copyright notices for more information.
15 
16 =========================================================================*/
17 #ifndef __itkCannyEdgeDetectionImageFilter_txx
18 #define __itkCannyEdgeDetectionImageFilter_txx
20 
23 #include "itkNumericTraits.h"
24 #include "itkProgressReporter.h"
26 
27 namespace itk
28 {
29 
30 template <class TInputImage, class TOutputImage>
33 {
34  unsigned int i;
35 
36  m_Variance.Fill(0.0);
37  m_MaximumError.Fill(0.01);
38 
43 
44  m_GaussianFilter = GaussianImageFilterType::New();
45  m_MultiplyImageFilter = MultiplyImageFilterType::New();
46  m_UpdateBuffer1 = OutputImageType::New();
47 
48  // Set up neighborhood slices for all the dimensions.
50  r.Fill(1);
51 
52  // Dummy neighborhood used to set up the slices.
54  it.SetRadius(r);
55 
56  // Slice the neighborhood
57  m_Center = it.Size() / 2;
58 
59  for (i = 0; i< ImageDimension; ++i)
60  {
61  m_Stride[i] = it.GetStride(i);
62  }
63 
64  for (i = 0; i< ImageDimension; ++i)
65  {
66  m_ComputeCannyEdgeSlice[i]
67  = std::slice( m_Center - m_Stride[i], 3, m_Stride[i]);
68  }
69 
70  // Allocate the derivative operator.
71  m_ComputeCannyEdge1stDerivativeOper.SetDirection(0);
72  m_ComputeCannyEdge1stDerivativeOper.SetOrder(1);
73  m_ComputeCannyEdge1stDerivativeOper.CreateDirectional();
74 
75  m_ComputeCannyEdge2ndDerivativeOper.SetDirection(0);
76  m_ComputeCannyEdge2ndDerivativeOper.SetOrder(2);
77  m_ComputeCannyEdge2ndDerivativeOper.CreateDirectional();
78 
79  //Initialize the list
80  m_NodeStore = ListNodeStorageType::New();
81  m_NodeList = ListType::New();
82 }
83 
84 template <class TInputImage, class TOutputImage>
85 void
88 {
89  // The update buffer looks just like the input.
90 
91  typename TInputImage::ConstPointer input = this->GetInput();
92 
93  m_UpdateBuffer1->CopyInformation( input );
94  m_UpdateBuffer1->SetRequestedRegion(input->GetRequestedRegion());
95  m_UpdateBuffer1->SetBufferedRegion(input->GetBufferedRegion());
96  m_UpdateBuffer1->Allocate();
97 }
98 
99 template <class TInputImage, class TOutputImage>
100 void
103 {
104  // call the superclass' implementation of this method
105  Superclass::GenerateInputRequestedRegion();
106  return;
107  // get pointers to the input and output
108  typename Superclass::InputImagePointer inputPtr =
109  const_cast< TInputImage * >( this->GetInput());
110  typename Superclass::OutputImagePointer outputPtr = this->GetOutput();
111 
112  if ( !inputPtr || !outputPtr )
113  {
114  return;
115  }
116 
117  //Set the kernel size.
118  // fix me this needs to be based on the variance of the gaussian filter
119  typename TInputImage::SizeValueType radius = 1;
120 
121  // get a copy of the input requested region (should equal the output
122  // requested region)
123  typename TInputImage::RegionType inputRequestedRegion;
124  inputRequestedRegion = inputPtr->GetRequestedRegion();
125 
126  // pad the input requested region by the operator radius
127  inputRequestedRegion.PadByRadius( radius );
128 
129  // crop the input requested region at the input's largest possible region
130  if ( inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion()) )
131  {
132  inputPtr->SetRequestedRegion( inputRequestedRegion );
133  return;
134  }
135  else
136  {
137  // Couldn't crop the region (requested region is outside the largest
138  // possible region). Throw an exception.
139 
140  // store what we tried to request (prior to trying to crop)
141  inputPtr->SetRequestedRegion( inputRequestedRegion );
142 
143  // build an exception
144  InvalidRequestedRegionError e(__FILE__, __LINE__);
145  OStringStream msg;
146  msg << this->GetNameOfClass()
147  << "::GenerateInputRequestedRegion()";
148  e.SetLocation(msg.str().c_str());
149  e.SetDescription("Requested region is (at least partially) outside the largest possible region.");
150  e.SetDataObject(inputPtr);
151  throw e;
152  }
153 }
154 
155 template< class TInputImage, class TOutputImage >
156 void
159  outputRegionForThread, int threadId)
160 {
162 
164 
165  void *globalData = 0;
166 
167  // Here input is the result from the gaussian filter
168  // output is the update buffer.
169  typename OutputImageType::Pointer input = m_GaussianFilter->GetOutput();
170  typename OutputImageType::Pointer output = this->GetOutput();
171 
172  // set iterator radius
173  Size<ImageDimension> radius; radius.Fill(1);
174 
175  // Find the data-set boundary "faces"
177  FaceListType faceList;
179  faceList = bC(input, outputRegionForThread, radius);
180 
182  FaceListType::iterator fit;
183 
184  // support progress methods/callbacks
185  ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels(), 100, 0.0f, 0.5f );
186 
187  // Process the non-boundady region and then each of the boundary faces.
188  // These are N-d regions which border the edge of the buffer.
189  for (fit=faceList.begin(); fit != faceList.end(); ++fit)
190  {
191  NeighborhoodType bit(radius, input, *fit);
192 
193  it = ImageRegionIterator<OutputImageType>(output, *fit);
194  bit.OverrideBoundaryCondition(&nbc);
195  bit.GoToBegin();
196 
197  while ( ! bit.IsAtEnd() )
198  {
199  it.Value() = ComputeCannyEdge(bit, globalData);
200  ++bit;
201  ++it;
202  progress.CompletedPixel();
203  }
204 
205  }
206 
207 }
208 
209 template< class TInputImage, class TOutputImage >
211 ::OutputImagePixelType
214  void * itkNotUsed(globalData) )
215 {
216  unsigned int i, j;
218 
219  OutputImagePixelType dx[ImageDimension];
220  OutputImagePixelType dxx[ImageDimension];
221  OutputImagePixelType dxy[ImageDimension*(ImageDimension-1)/2];
222  OutputImagePixelType deriv;
223  OutputImagePixelType gradMag;
224 
225  // double alpha = 0.01;
226 
227  //Calculate 1st & 2nd order derivative
228  for(i = 0; i < ImageDimension; i++)
229  {
230  dx[i] = innerProduct(m_ComputeCannyEdgeSlice[i], it,
231  m_ComputeCannyEdge1stDerivativeOper);
232  dxx[i] = innerProduct(m_ComputeCannyEdgeSlice[i], it,
233  m_ComputeCannyEdge2ndDerivativeOper);
234  }
235 
237  int k = 0;
238 
239  //Calculate the 2nd derivative
240  for(i = 0; i < ImageDimension-1; i++)
241  {
242  for(j = i+1; j < ImageDimension; j++)
243  {
244  dxy[k] = 0.25 * it.GetPixel(m_Center - m_Stride[i] - m_Stride[j])
245  - 0.25 * it.GetPixel(m_Center - m_Stride[i]+ m_Stride[j])
246  -0.25 * it.GetPixel(m_Center + m_Stride[i] - m_Stride[j])
247  +0.25 * it.GetPixel(m_Center + m_Stride[i] + m_Stride[j]);
248 
249  deriv += 2.0 * dx[i]*dx[j]*dxy[k];
250  k++;
251  }
252  }
253 
254  gradMag = 0.0001; // alpha * alpha;
255  for (i = 0; i < ImageDimension; i++)
256  {
257  deriv += dx[i] * dx[i] * dxx[i];
258  gradMag += dx[i] * dx[i];
259  }
260 
261  deriv = deriv/gradMag;
262 
263  return deriv;
264 }
265 
266 // Calculate the second derivative
267 template< class TInputImage, class TOutputImage >
268 void
271 {
272  CannyThreadStruct str;
273  str.Filter = this;
274 
275  this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads());
276  this->GetMultiThreader()->SetSingleMethod(this->Compute2ndDerivativeThreaderCallback, &str);
277 
278  this->GetMultiThreader()->SingleMethodExecute();
279 }
280 
281 template<class TInputImage, class TOutputImage>
285 {
286  CannyThreadStruct *str;
287 
288  int total, threadId, threadCount;
289 
290  threadId = ((MultiThreader::ThreadInfoStruct *)(arg))->ThreadID;
291  threadCount = ((MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads;
292 
293  str = (CannyThreadStruct *)(((MultiThreader::ThreadInfoStruct *)(arg))->UserData);
294 
295  // Execute the actual method with appropriate output region
296  // first find out how many pieces extent can be split into.
297  // Using the SplitRequestedRegion method from itk::ImageSource.
298  OutputImageRegionType splitRegion;
299  total = str->Filter->SplitRequestedRegion(threadId, threadCount,
300  splitRegion);
301 
302  if (threadId < total)
303  {
304  str->Filter->ThreadedCompute2ndDerivative(splitRegion, threadId);
305  }
306 
308 }
309 
310 template< class TInputImage, class TOutputImage >
311 void
314 {
315  // Allocate the output
316  this->GetOutput()->SetBufferedRegion( this->GetOutput()->GetRequestedRegion() );
317  this->GetOutput()->Allocate();
318 
319  typename InputImageType::ConstPointer input = this->GetInput();
320 
323 
326 
329 
330  this->AllocateUpdateBuffer();
331 
332  // 1.Apply the Gaussian Filter to the input image.-------
333  m_GaussianFilter->SetVariance(m_Variance);
334  m_GaussianFilter->SetMaximumError(m_MaximumError);
335  m_GaussianFilter->SetInput(input);
336  // modify to force excution, due to grafting complications
337  m_GaussianFilter->Modified();
338  m_GaussianFilter->Update();
339 
340  //2. Calculate 2nd order directional derivative-------
341  // Calculate the 2nd order directional derivative of the smoothed image.
342  // The output of this filter will be used to store the directional
343  // derivative.
344  this->Compute2ndDerivative();
345 
346  this->Compute2ndDerivativePos();
347 
348 
349  // 3. Non-maximum suppression----------
350 
351  // Calculate the zero crossings of the 2nd directional derivative and write
352  // the result to output buffer.
353  zeroCrossFilter->SetInput(this->GetOutput());
354  zeroCrossFilter->Update();
355 
356  // 4. Hysteresis Thresholding---------
357 
358  // First get all the edges corresponding to zerocrossings
359  m_MultiplyImageFilter->SetInput1(m_UpdateBuffer1);
360  m_MultiplyImageFilter->SetInput2(zeroCrossFilter->GetOutput());
361 
362  // To save memory, we will graft the output of the m_GaussianFilter,
363  // which is no longer needed, into the m_MultiplyImageFilter.
364  m_MultiplyImageFilter->GraftOutput( m_GaussianFilter->GetOutput() );
365  m_MultiplyImageFilter->Update();
366 
367  //Then do the double threshoulding upon the edge reponses
368  this->HysteresisThresholding();
369 }
370 
371 template< class TInputImage, class TOutputImage >
372 void
375 {
376  // This is the Zero crossings of the Second derivative multiplied with the
377  // gradients of the image. HysteresisThresholding of this image should give
378  // the Canny output.
379  typename OutputImageType::Pointer input = m_MultiplyImageFilter->GetOutput();
380  float value;
381 
382  ListNodeType *node;
383 
384 // fix me
385  ImageRegionIterator<TOutputImage> oit( input, input->GetRequestedRegion() );
386 
387  oit.GoToBegin();
388 
389  ImageRegionIterator<TOutputImage> uit(this->GetOutput(),
390  this->GetOutput()->GetRequestedRegion());
391  uit.GoToBegin();
392  while(!uit.IsAtEnd())
393  {
395  ++uit;
396  }
397 
398  while(!oit.IsAtEnd())
399  {
400  value = oit.Value();
401 
402  if(value > m_UpperThreshold)
403  {
404  node = m_NodeStore->Borrow();
405  node->m_Value = oit.GetIndex();
406  m_NodeList->PushFront(node);
407  FollowEdge(oit.GetIndex());
408  }
409 
410  ++oit;
411  }
412 }
413 
414 template< class TInputImage, class TOutputImage >
415 void
418 {
419  // This is the Zero crossings of the Second derivative multiplied with the
420  // gradients of the image. HysteresisThresholding of this image should give
421  // the Canny output.
422  typename OutputImageType::Pointer input = m_MultiplyImageFilter->GetOutput();
423  InputImageRegionType inputRegion = input->GetRequestedRegion();
424 
425  IndexType nIndex;
426  IndexType cIndex;
427  ListNodeType * node;
428 
429  //assign iterator radius
430  Size<ImageDimension> radius;
431  radius.Fill(1);
432 
434  input, input->GetRequestedRegion());
435  ImageRegionIteratorWithIndex<TOutputImage> uit( this->GetOutput(),
436  this->GetOutput()->GetRequestedRegion());
437 
438  uit.SetIndex(index);
440  {
441  // we must remove the node if we are not going to follow it!
442 
443  // Pop the front node from the list and read its index value.
444  node = m_NodeList->Front(); // get a pointer to the first node
445  m_NodeList->PopFront(); // unlink the front node
446  m_NodeStore->Return(node); // return the memory for reuse
447  return;
448  }
449 
450  int nSize = m_Center * 2 +1;
451  while(!m_NodeList->Empty())
452  {
453  // Pop the front node from the list and read its index value.
454  node = m_NodeList->Front(); // get a pointer to the first node
455  cIndex = node->m_Value; // read the value of the first node
456  m_NodeList->PopFront(); // unlink the front node
457  m_NodeStore->Return(node); // return the memory for reuse
458 
459  // Move iterators to the correct index position.
460  oit.SetLocation(cIndex);
461  uit.SetIndex(cIndex);
462  uit.Value() = 1;
463 
464  // Search the neighbors for new indicies to add to the list.
465  for(int i = 0; i < nSize; i++)
466  {
467  nIndex = oit.GetIndex(i);
468  uit.SetIndex(nIndex);
469  if(inputRegion.IsInside(nIndex))
470  {
471  if(oit.GetPixel(i) > m_LowerThreshold && uit.Value() != NumericTraits<OutputImagePixelType>::One )
472  {
473  node = m_NodeStore->Borrow(); // get a new node struct
474  node->m_Value = nIndex; // set its value
475  m_NodeList->PushFront(node); // add the new node to the list
476 
477  uit.SetIndex(nIndex);
479  }
480  }
481  }
482  }
483 }
484 
485 
486 template< class TInputImage, class TOutputImage >
487 void
489 ::ThreadedCompute2ndDerivativePos(const OutputImageRegionType& outputRegionForThread, int threadId)
490 {
491 
493 
496 
498 
499  // Here input is the result from the gaussian filter
500  // input1 is the 2nd derivative result
501  // output is the gradient of 2nd derivative
502  typename OutputImageType::Pointer input1 = this->GetOutput();
503  typename OutputImageType::Pointer input = m_GaussianFilter->GetOutput();
504 
505  typename InputImageType::Pointer output = m_UpdateBuffer1;
506 
507 
508  // set iterator radius
509  Size<ImageDimension> radius; radius.Fill(1);
510 
511  // Find the data-set boundary "faces"
513  FaceListType faceList;
515  faceList = bC(input, outputRegionForThread, radius);
516 
518  FaceListType::iterator fit;
519 
520  // support progress methods/callbacks
521  ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels(), 100, 0.5f, 0.5f);
522 
524 
525  OutputImagePixelType dx[ImageDimension];
526  OutputImagePixelType dx1[ImageDimension];
527 
528  OutputImagePixelType directional[ImageDimension];
529  OutputImagePixelType derivPos;
530 
531  OutputImagePixelType gradMag;
532 
533  // Process the non-boundary region and then each of the boundary faces.
534  // These are N-d regions which border the edge of the buffer.
535 
537 
538  for (fit=faceList.begin(); fit != faceList.end(); ++fit)
539  {
541  input, *fit);
543  input1, *fit);
544  it = ImageRegionIterator<OutputImageType>(output, *fit);
545  bit.OverrideBoundaryCondition(&nbc);
546  bit.GoToBegin();
547  bit1.GoToBegin();
548  it.GoToBegin();
549 
550  while ( ! bit.IsAtEnd() )
551  {
552 
553  gradMag = 0.0001;
554 
555  for ( unsigned int i = 0; i < ImageDimension; i++)
556  {
557  dx[i] = IP(m_ComputeCannyEdgeSlice[i], bit,
558  m_ComputeCannyEdge1stDerivativeOper);
559  gradMag += dx[i] * dx[i];
560 
561  dx1[i] = IP(m_ComputeCannyEdgeSlice[i], bit1,
562  m_ComputeCannyEdge1stDerivativeOper);
563  }
564 
565  gradMag = vcl_sqrt((double)gradMag);
566  derivPos = zero;
567  for ( unsigned int i = 0; i < ImageDimension; i++)
568  {
569 
570  //First calculate the directional derivative
571 
572  directional[i] = dx[i]/gradMag;
573 
574  //calculate gradient of 2nd derivative
575 
576  derivPos += dx1[i] * directional[i];
577  }
578 
579  it.Value() = ((derivPos <= zero));
580  it.Value() = it.Get() * gradMag;
581  ++bit;
582  ++bit1;
583  ++it;
584  progress.CompletedPixel();
585  }
586 
587  }
588 }
589 
590 //Calculate the second derivative
591 template< class TInputImage, class TOutputImage >
592 void
595 {
596  CannyThreadStruct str;
597  str.Filter = this;
598 
599  this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads());
600  this->GetMultiThreader()->SetSingleMethod(this->Compute2ndDerivativePosThreaderCallback, &str);
601 
602  this->GetMultiThreader()->SingleMethodExecute();
603 }
604 
605 template<class TInputImage, class TOutputImage>
609 {
610  CannyThreadStruct *str;
611 
612  int total, threadId, threadCount;
613 
614  threadId = ((MultiThreader::ThreadInfoStruct *)(arg))->ThreadID;
615  threadCount = ((MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads;
616 
617  str = (CannyThreadStruct *)(((MultiThreader::ThreadInfoStruct *)(arg))->UserData);
618 
619  // Execute the actual method with appropriate output region
620  // first find out how many pieces extent can be split into.
621  // Using the SplitRequestedRegion method from itk::ImageSource.
622 
623  OutputImageRegionType splitRegion;
624  total = str->Filter->SplitRequestedRegion(threadId, threadCount,
625  splitRegion);
626 
627  if (threadId < total)
628  {
629  str->Filter->ThreadedCompute2ndDerivativePos( splitRegion, threadId);
630  }
631 
633 }
634 
635 template <class TInputImage, class TOutputImage>
636 void
638 ::PrintSelf(std::ostream& os, Indent indent) const
639 {
640  Superclass::PrintSelf(os,indent);
641 
642  os << "Variance: " << m_Variance << std::endl;
643  os << "MaximumError: " << m_MaximumError << std::endl;
644  os << indent << "Threshold: "
645  << static_cast<typename NumericTraits<OutputImagePixelType>::PrintType>(m_Threshold)
646  << std::endl;
647  os << indent << "UpperThreshold: "
648  << static_cast<typename NumericTraits<OutputImagePixelType>::PrintType>(m_UpperThreshold)
649  << std::endl;
650  os << indent << "LowerThreshold: "
651  << static_cast<typename NumericTraits<OutputImagePixelType>::PrintType>(m_LowerThreshold)
652  << std::endl;
653  os << indent << "OutsideValue: "
654  << static_cast<typename NumericTraits<OutputImagePixelType>::PrintType>(m_OutsideValue)
655  << std::endl;
656  os << "Center: "
657  << m_Center << std::endl;
658  os << "Stride: "
659  << m_Stride << std::endl;
660  os << "Gaussian Filter: " << std::endl;
661  m_GaussianFilter->Print(os,indent.GetNextIndent());
662  os << "Multiply image Filter: " << std::endl;
663  m_MultiplyImageFilter->Print(os,indent.GetNextIndent());
664  os << "UpdateBuffer1: " << std::endl;
665  m_UpdateBuffer1->Print(os,indent.GetNextIndent());
666 }
667 
668 }//end of itk namespace
669 #endif

Generated at Sat May 11 2013 23:33:48 for Orfeo Toolbox with doxygen 1.8.3.1