[Insight-users] an Image To Image to compute the gradient of the popular l2 approximation of the total variation semi norm

devieill at irit.fr devieill at irit.fr
Thu Sep 23 12:13:03 EDT 2010


Hi all,

I am currently rewriting some of my code, and as a matter of fact it find
it iteresting to write a filter to easily compute the gradient of the
total variation. I have already done this computation with a standard itk
pipeline, at a high cost (swapping) for large image.
So starting from itkGradientMagnitudeImageFilter I have decided to give it
a try. Right now I am bit stuck at computing the properly the second
derivative pass. To explain better from an image I with 2 dimensions, one
has to get the partial derivatives dI/dx, dI/dy and then to form 2 images
:
I1 = (dI/dx)/( (dI/dx)^2 + (dI/dy)^2 + beta^2)
I2 = (dI/dy)/( (dI/dx)^2 + (dI/dy)^2 + beta^2)
And finally to compute the result R:
R = dI1/dx + dI2/dy

this part is a bit tricky to me since I am not sure to have the proper
images to compute it, that is to say either I1 or I2 have size 1 on one of
the coordinates. Assuming I do a 2nd derivative, I should request a region
padded by a radius of 2 right ?

However I am not too sure about how to doing it properly and more
importantly if that is the critical point. Here is the important part of
the code doing this.

Any help would be much appreciated !!

Regards,
de Vieilleville François


template< class TInputImage, class TOutputImage>
void
GradientTotalVariationL2ApproximationImageFilter< TInputImage, TOutputImage >
::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread,
                       int threadId)
{
  //////////////////////////////////////////////
  //  create copies for storing the returned values
  typename TOutputImage::Pointer ddx[ImageDimension];

  //allocate copies
  for (unsigned int i = 0; i < ImageDimension; ++i) {
    ddx[i] = TOutputImage::New();
    ddx[i]->SetRegions(outputRegionForThread );
    ddx[i]->Allocate();
  }

  OutputPixelType grad_tv;
  ZeroFluxNeumannBoundaryCondition<InputImageType> nbc;

  ConstNeighborhoodIterator<InputImageType> nit;
  ConstNeighborhoodIterator<TInputImage> bit;
  ImageRegionIterator<OutputImageType> it;

  NeighborhoodInnerProduct<InputImageType, RealType> SIP;

  // Get the input and output
  OutputImageType *       outputImage = this->GetOutput();
  const InputImageType *  inputImage  = this->GetInput();

  // Set up operators
  DerivativeOperator<RealType, ImageDimension> op[ImageDimension];

  for (int i = 0; i< ImageDimension; i++)
    {
    op[i].SetDirection(0);
    op[i].SetOrder(1);
    op[i].CreateDirectional();

    // Reverse order of coefficients for the convolution with the image to
    // follow.
    //op[i].FlipAxes();

    // Take into account the pixel spacing if necessary
    if (m_UseImageSpacing == true)
      {
      if ( this->GetInput()->GetSpacing()[i] == 0.0 )
        {
        itkExceptionMacro(<< "Image spacing cannot be zero.");
        }
      else
        {
        op[i].ScaleCoefficients( 1.0 / this->GetInput()->GetSpacing()[i] );
        }
      }
    }

  // Calculate iterator radius
  Size<ImageDimension> radius;
  for (unsigned int i = 0; i < ImageDimension; ++i)
    {
      radius[i]  = op[0].GetRadius()[0];
      //std::cout << "radius["<<i<<"]=" << radius[i] << std::endl;
    }

  // Find the data-set boundary "faces"
  typename
NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<TInputImage>::FaceListType
faceList;
  NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<TInputImage> bC;
  faceList = bC(inputImage, outputRegionForThread, radius);

  typename
NeighborhoodAlgorithm::ImageBoundaryFacesCalculator<InputImageType>::FaceListType::iterator
fit;
  fit = faceList.begin();

  // support progress methods/callbacks
  ProgressReporter progress(this, threadId,
outputRegionForThread.GetNumberOfPixels());

  // Initialize the x_slice array
  nit = ConstNeighborhoodIterator<InputImageType>(radius, inputImage, *fit);

  //////////////////////////////////////////////

  std::slice x_slice[ImageDimension];
  const unsigned long center = nit.Size() / 2;
  for (unsigned int i = 0; i < ImageDimension; ++i)
    {

      x_slice[i] = std::slice( center - nit.GetStride(i) * radius[0],
				 op[i].GetSize()[0], nit.GetStride(i));
    }
  // Process non-boundary face and then each of the boundary faces.
  // These are N-d regions which border the edge of the buffer.

  // set the proper iterators on the copied regions
  ImageRegionIterator<OutputImageType> it_copy[ImageDimension];
  //  typename TInputImage::RegionType region_on_copy_current_face;

  for (fit = faceList.begin(); fit != faceList.end(); ++fit)
    {
      bit = ConstNeighborhoodIterator<InputImageType>(radius,
						      inputImage, *fit);
      it = ImageRegionIterator<OutputImageType>(outputImage, *fit);
      // set the copied regions iterators
      for (unsigned int j =0; j < ImageDimension; ++j) {
	it_copy[j] = ImageRegionIterator<InputImageType>(ddx[j], *fit);
      }

      bit.OverrideBoundaryCondition(&nbc);
      bit.GoToBegin();

      while ( ! bit.IsAtEnd() )
	{
	  RealType a = NumericTraits<RealType>::Zero;
	  RealType deriv[ImageDimension];
	  for (int i = 0; i < ImageDimension; ++i)
	    {
	      const RealType g = SIP(x_slice[i], bit, op[i]);
	      deriv[i] = g;
	      a += g * g;
	      // vcl_pow(val,2.);
	    }
	  it.Value() = vcl_sqrt(a + m_BetaSquare);
	  for (unsigned int j =0; j < ImageDimension; ++j) {
	    it_copy[j].Value() = deriv[j]/it.Get();
	    ++(it_copy[j]);
	  }
	  ++bit;
	  ++it;
	}
    }

  faceList = bC(ddx[0], outputRegionForThread, radius); // assuming they
are all the same for the ddx[i]....
  fit = faceList.begin();
  ConstNeighborhoodIterator<InputImageType> nit_copy[ImageDimension];
  ConstNeighborhoodIterator<InputImageType> bit_copy[ImageDimension];

  for ( unsigned int i =0; i < ImageDimension; ++i) {
    nit_copy[i] = ConstNeighborhoodIterator<InputImageType>(radius,
							    ddx[i], *fit);
  }
  // Initialize the x_slice_copy array
  std::slice x_slice_copy[ImageDimension];
  for (unsigned int i = 0; i < ImageDimension; ++i) {
    const unsigned long center = nit_copy[i].Size() / 2;
    x_slice_copy[i] = std::slice( center - nit_copy[i].GetStride(i) *
radius[0],
				  op[i].GetSize()[0], nit_copy[i].GetStride(i));
  }

  for (fit = faceList.begin(); fit != faceList.end(); ++fit)
    {
      for ( unsigned int i =0; i < ImageDimension; ++i) {
	bit_copy[i] = ConstNeighborhoodIterator<InputImageType>(radius,
								ddx[i], *fit);
	bit_copy[i].OverrideBoundaryCondition(&nbc);
	bit_copy[i].GoToBegin();
      }
      it = ImageRegionIterator<OutputImageType>(outputImage, *fit);
      //bool nit_copy_are_at_end = false;
      while ( !bit_copy[0].IsAtEnd() )
	{
	  grad_tv =0.;
	  for (unsigned int i = 0; i < ImageDimension; ++i)
	    {
	      // compute derivative of previous computed region
	      double val = SIP(x_slice_copy[i], bit_copy[i], op[i]);
	      grad_tv += val;
	    }
	  it.Value() = grad_tv;
	  for (unsigned int j =0; j < ImageDimension; ++j) {
	    ++(bit_copy[j]);
	  }
	  ++it;
	  progress.CompletedPixel();
	}
    }
}



More information about the Insight-users mailing list