vnl_matrix<double> FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections>
::CalcShBasis(vnl_matrix<double>& sphCoords)
{
    int M = sphCoords.rows();
    int j, m; double mag, plm;
    vnl_matrix<double> shBasis;
    shBasis.set_size(M, m_NumCoeffs);

    for (int p=0; p<M; p++)
    {
        j=0;
        for (int l=0; l<=ShOrder; l=l+2)
            for (m=-l; m<=l; m++)
            {
                switch (m_Toolkit)
                {
                case FSL:
                plm = legendre_p<double>(l,abs(m),cos(sphCoords(p,0)));
                mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial<double>(l-abs(m))/factorial<double>(l+abs(m)))*plm;

                if (m<0)
                    shBasis(p,j) = sqrt(2.0)*mag*cos(fabs((double)m)*sphCoords(p,1));
                else if (m==0)
                    shBasis(p,j) = mag;
                else
                    shBasis(p,j) = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(p,1));
                    break;
                case MRTRIX:

                plm = legendre_p<double>(l,abs(m),-cos(sphCoords(p,0)));
                mag = sqrt((double)(2*l+1)/(4.0*M_PI)*factorial<double>(l-abs(m))/factorial<double>(l+abs(m)))*plm;
                if (m>0)
                    shBasis(p,j) = mag*cos(m*sphCoords(p,1));
                else if (m==0)
                    shBasis(p,j) = mag;
                else
                    shBasis(p,j) = mag*sin(-m*sphCoords(p,1));
                    break;
                }

                j++;
            }
    }
    return shBasis;
}
void FiniteDiffOdfMaximaExtractionFilter< PixelType, ShOrder, NrOdfDirections>
::ThreadedGenerateData( const OutputImageRegionType& outputRegionForThread, ThreadIdType threadID )
{
  typename CoefficientImageType::Pointer ShCoeffImage = static_cast< CoefficientImageType* >( this->ProcessObject::GetInput(0) );

  ImageRegionConstIterator< CoefficientImageType > cit(ShCoeffImage, outputRegionForThread );

  OdfType odf;
  while( !cit.IsAtEnd() )
  {
    typename CoefficientImageType::IndexType idx3 = cit.GetIndex();
    if (m_MaskImage->GetPixel(idx3)==0)
    {
      ++cit;
      continue;
    }

    CoefficientPixelType c = cit.Get();

    // calculate ODF
    double max = 0;
    odf.Fill(0.0);
    for (int i=0; i<NrOdfDirections; i++)
    {
      for (int j=0; j<m_NumCoeffs; j++)
        odf[i] += c[j]*m_ShBasis(i,j);
      if (odf[i]>max)
        max = odf[i];
    }
    if (max<0.0001)
    {
      ++cit;
      continue;
    }

    std::vector< DirectionType > candidates, peaks, temp;
    peaks.clear();
    max *= m_PeakThreshold;                         // relative threshold
    FindCandidatePeaks(odf, max, candidates);       // find all local maxima
    candidates = MeanShiftClustering(candidates);   // cluster maxima

    vnl_matrix<double> sphCoords;
    CreateDirMatrix(candidates, sphCoords);                // convert candidate peaks to spherical angles
    vnl_matrix< float > shBasis;
    if (m_Toolkit==Toolkit::MRTRIX)
      shBasis = mitk::sh::CalcShBasisForDirections(ShOrder, sphCoords);
    else
      shBasis = mitk::sh::CalcShBasisForDirections(ShOrder, sphCoords, false);

    max = 0.0;
    for (unsigned int i=0; i<candidates.size(); i++)         // scale peaks according to ODF value
    {
      double val = 0;
      for (int j=0; j<m_NumCoeffs; j++)
        val += c[j]*shBasis(i,j);
      if (val>max)
        max = val;
      peaks.push_back(candidates[i]*val);
    }
    std::sort( peaks.begin(), peaks.end(), CompareVectors );  // sort peaks

    // kick out directions to close to a larger direction (too far away to cluster but too close to keep)
    unsigned int m = peaks.size();
    if ( m>m_MaxNumPeaks )
      m = m_MaxNumPeaks;
    for (unsigned int i=0; i<m; i++)
    {
      DirectionType v1 = peaks.at(i);
      double val = v1.magnitude();
      if (val<max*m_PeakThreshold || val<m_AbsolutePeakThreshold)
        break;

      bool flag = true;
      for (unsigned int j=0; j<peaks.size(); j++)
        if (i!=j)
        {
          DirectionType v2 = peaks.at(j);
          double val2 = v2.magnitude();
          double angle = fabs(dot_product(v1,v2)/(val*val2));
          if (angle>m_AngularThreshold && val<val2)
          {
            flag = false;
            break;
          }
        }

      if (flag)
        temp.push_back(v1);
    }
    peaks = temp;

    itk::Index<4> idx4; idx4[0] = idx3[0]; idx4[1] = idx3[1]; idx4[2] = idx3[2];

    // fill output image
    unsigned int num = peaks.size();
    if ( num>m_MaxNumPeaks )
      num = m_MaxNumPeaks;
    for (unsigned int i=0; i<num; i++)
    {
      DirectionType dir = peaks.at(i);
      switch (m_NormalizationMethod)
      {
      case NO_NORM:
        break;
      case SINGLE_VEC_NORM:
        dir.normalize();
        break;
      case MAX_VEC_NORM:
        dir /= max;
        break;
      }

      if (m_ApplyDirectionMatrix)
        dir = m_MaskImage->GetDirection()*dir;

      if (m_FlipX)
        dir[0] = -dir[0];
      if (m_FlipY)
        dir[1] = -dir[1];
      if (m_FlipZ)
        dir[2] = -dir[2];

      for (unsigned int j = 0; j<3; j++)
      {
        idx4[3] = i*3 + j;
        m_PeakImage->SetPixel(idx4, dir[j]);
      }
    }
    m_NumDirectionsImage->SetPixel(idx3, num);
    ++cit;
  }
  MITK_INFO << "Thread " << threadID << " finished extraction";
}