void 
cmtk::FitSplineWarpToDeformationField::FitSpline( SplineWarpXform& splineWarp, const int nLevels )
{
  // loop until final control point spacing
  for ( int level = 0; level < nLevels; ++level )
    {
    DebugOutput( 5 ) << "Multi-resolution spline fitting level " << level+1 << " out of " << nLevels << "\n";
    // refine control point grid unless this is first iteration
    if ( level )
      {
      splineWarp.Refine();
      }

    DebugOutput( 6 ) << "  Control point grid is " << splineWarp.m_Dims[0] << "x" << splineWarp.m_Dims[1] << "x" << splineWarp.m_Dims[2] << "\n";

    // compute residuals
    splineWarp.RegisterVolumePoints( this->m_DeformationField->m_Dims, this->m_DeformationField->m_Spacing, this->m_DeformationField->m_Offset );
    this->CreateGridLookupTables( splineWarp );
    this->ComputeResiduals( splineWarp );
    
    // loop over all control points to compute deltas as the spline coefficients that fit current residuals
    std::vector< FixedVector<3,Types::Coordinate> > delta( splineWarp.m_NumberOfControlPoints, FixedVector<3,Types::Coordinate>( FixedVector<3,Types::Coordinate>::Init( 0.0 ) ) );

    const WarpXform::ControlPointRegionType cpRegionAll = splineWarp.GetAllControlPointsRegion();
#ifndef _OPENMP
    const WarpXform::ControlPointRegionType cpRegion = cpRegionAll;
#else // _OPENMP
    const size_t maxIdx = (cpRegionAll.To()-cpRegionAll.From()).MaxIndex();
    const int sliceFrom = cpRegionAll.From()[maxIdx];
    const int sliceTo = cpRegionAll.To()[maxIdx];
#pragma omp parallel for
    for ( int slice = sliceFrom; slice < sliceTo; ++slice )
      {
      WarpXform::ControlPointRegionType cpRegion = cpRegionAll;
      cpRegion.From()[maxIdx] = slice;
      cpRegion.To()[maxIdx] = slice+1;
#endif
      // this is the loop over all (i_1,i_2,i_3) control point indexes
      for ( RegionIndexIterator<WarpXform::ControlPointRegionType> cpIt( cpRegion); cpIt != cpIt.end(); ++cpIt )
	{
	const size_t cp = splineWarp.GetOffsetFromIndex( cpIt.Index() ) / 3;
	
	// volume of influence for the current control point
	const DataGrid::RegionType voi = this->GetDeformationGridRange( splineWarp, cpIt.Index() );
	
	// iterate over all voxels, "c", influenced by current control point.
	Types::Coordinate normalize = 0;
	for ( RegionIndexIterator<DataGrid::RegionType> it( voi ); it != it.end(); ++it )
	  {
	  const DataGrid::IndexType idx = it.Index();
	  
	  // Enumerator of Eq. (8) - this is a vector
	  Types::Coordinate pB = 1; // this is the product over the B in enumerator of Eq. (8)
	  for ( int axis = 0; axis < 3; ++axis )
	    {
	    // relative index of spline function for current pixel relative to current control point
	    const int relIdx = cpIt.Index()[axis] - splineWarp.m_GridIndexes[axis][idx[axis]];
	    // sanity range checks
	    assert( (relIdx >= 0) && (relIdx < 4) );
	    
	    pB *= splineWarp.m_GridSpline[axis][4*it.Index()[axis]+relIdx];
	    }
	  
	  // Denominator of Eq. (8) - this is a scalar
	  Types::Coordinate dPc = 0;
	  
	  const DataGrid::RegionType neighborhood( DataGrid::IndexType::Init( 0 ), DataGrid::IndexType::Init( 4 ) );
	  for ( RegionIndexIterator<DataGrid::RegionType> nIt( neighborhood ); nIt != nIt.end(); ++nIt )
	    {
	    Types::Coordinate prod = 1;
	    for ( int axis = 0; axis < 3; ++axis )
	      {
	      prod *= splineWarp.m_GridSpline[axis][(4*it.Index()[axis])+nIt.Index()[axis]];
	      }
	    
	    dPc += MathUtil::Square( prod );
	    }
	  
	  // Eq. (11)
	  const Types::Coordinate pB2 = MathUtil::Square( pB );
	  delta[cp] += pB2 * (pB / dPc) * this->m_Residuals[this->m_DeformationField->GetOffsetFromIndex( idx )/3]; // S_c(u1...un)
	  normalize += pB2;
	  }
	
	// Eq. (11) denominator
	delta[cp] /= normalize;
	}
#ifdef _OPENMP
      }
#endif
    
    // apply delta
    for ( size_t cp = 0; cp < splineWarp.m_NumberOfControlPoints; ++cp )
      {
      splineWarp.SetShiftedControlPointPositionByOffset( splineWarp.GetShiftedControlPointPositionByOffset( cp ) + delta[cp], cp );
      }
    }
}
