16 #ifndef __itkOdfMaximaExtractionFilter_cpp
17 #define __itkOdfMaximaExtractionFilter_cpp
21 #include <itkImageRegionIterator.h>
22 #include <itkContinuousIndex.h>
24 #include <vtkSmartPointer.h>
25 #include <vtkPolyData.h>
26 #include <vtkCellArray.h>
27 #include <vtkPoints.h>
28 #include <vtkPolyLine.h>
30 #include <boost/progress.hpp>
31 #include <boost/math/special_functions.hpp>
32 #include <vnl/vnl_det.h>
33 #include <vnl/vnl_trace.h>
40 template<
class TOdfPixelType >
42 m_NormalizationMethod(MAX_VEC_NORM),
46 m_OutputFiberBundle(NULL),
47 m_NumDirectionsImage(NULL),
48 m_DirectionImageContainer(NULL)
54 template<
class TOdfPixelType >
57 if (m_ShCoeffImage.IsNotNull())
59 cout <<
"Using preset coefficient image\n";
63 cout <<
"Starting qball reconstruction\n";
66 filter->SetGradientImage( m_DiffusionGradients, m_DiffusionImage );
67 filter->SetBValue(m_Bvalue);
68 filter->SetLambda(0.006);
69 filter->SetNormalizationMethod(QballReconstructionFilterType::QBAR_SOLID_ANGLE);
71 m_ShCoeffImage = filter->GetCoefficientImage();
72 if (m_ShCoeffImage.IsNull())
83 template<
class TOdfPixelType >
85 ::SolveCubic(
const double& a,
const double& b,
const double& c,
const double& d)
87 double A, B, p, q, r, D,
offset, ee, tmp, root;
89 double inv3 = 1.0/3.0;
93 p = b/a; q = c/a; r = d/a;
95 B = (2.0*p*p*p-9.0*p*q+27.0*r)/27.0;
104 tmp = -B+ee; root = cbrt(tmp);
105 tmp = -B-ee; root += cbrt(tmp);
106 root -=
offset; roots.push_back(root);
112 double angle = 2.0*inv3*atan(ee/(sqrt(tmp2*tmp2+ee*ee)+tmp2));
113 double sqrt3 = sqrt(3.0);
117 root = 2*ee*tmp-
offset; roots.push_back(root);
118 root = -ee*(tmp+sqrt3*
tmp2)-offset; roots.push_back(root);
119 root = -ee*(tmp-sqrt3*
tmp2)-offset; roots.push_back(root);
125 root=2*tmp-
offset; roots.push_back(root);
127 root=-tmp-
offset; roots.push_back(root);
136 root = (-c+tmp)/(2.0*b); roots.push_back(root);
137 root = (-c-tmp)/(2.0*b); roots.push_back(root);
140 root = -c/(2.0*b); roots.push_back(root);
143 root = -d/c; roots.push_back(root);
148 template<
class TOdfPixelType >
150 ::ODF_dtheta(
const double& sn,
const double& cs,
const double& A,
const double& B,
const double& C,
const double& D,
const double& E,
const double& F,
const double& G,
const double& H)
152 double dtheta=(G-7*E)*sn*sn + (7*F-35*D-H)*sn*cs + (H+C-F-3*A-5*D)*sn + (0.5*E+B+0.5*G)*cs -0.5*G+3.5*E;
156 template<
class TOdfPixelType >
158 ::ODF_dtheta2(
const double& sn,
const double& cs,
const double& A,
const double& B,
const double& C,
const double& D,
const double& E,
const double& F,
const double& G,
const double& H)
160 double dtheta2=4*(G-7*E)*sn*cs + 2*(7*F-35*D-H)*(2*cs*cs-1) + 2*(H+C-F-3*A-5*D)*cs -(E+2*B+G)*sn;
164 template<
class TOdfPixelType >
166 ::ODF_dphi2(
const double& sn,
const double& cs,
const double& A,
const double& B,
const double& C,
const double& D,
const double& E,
const double& F,
const double& G,
const double& H)
168 double dphi2=35*D*((1+cs)*(1+cs)/4)+(3*A-30*D)*(1+cs)/2.0+3*D-A + 0.5*(7*E*(1+cs)/2.0-3*E+B)*sn + (7*F*(1+cs)/2+C-F)*(1-cs)/2.0 + G*sn*(1-cs)/4.0 + H*((1-cs)*(1-cs)/4);
172 template<
class TOdfPixelType >
176 const double thr = 0.03;
177 const double phi_step = 0.005;
179 double mag, Y, Yp, sn, cs;
181 double A, B, C, D, E, F, G, H, Bp, Cp, Ep, Fp, Gp, Hp, Bs, Cs, Es, Fs, Gs, Hs;
183 a = SHcoeff; ap = SHcoeff;
185 m_CandidatePeaks.clear();
187 for (
int adaptiveStepwidth=0; adaptiveStepwidth<=1; adaptiveStepwidth++)
193 for (
int l=0; l<=4; l=l+2)
195 for (
int m=-l; m<=l; m++)
200 mag = sqrt(((2*l+1)/(2*
M_PI))*factorial<double>(l+m)/factorial<double>(l-m));
202 Yp = -m*mag*sin(m*phi);
206 Y = sqrt((2*l+1)/(4*
M_PI));
211 mag = pow(-1.0,m)*sqrt(((2*l+1)/(2*
M_PI))*factorial<double>(l-m)/factorial<double>(l+m));
213 Yp = m*mag*cos(m*phi);
216 ap[j] = SHcoeff[j]*Yp;
221 A=0.5*a[3]; B=-3*(a[2]+a[4]); C=3*(a[1]+a[5]); D=0.125*a[10]; E=-2.5*(a[9]+a[11]);
222 F=7.5*(a[8]+a[12]); G=-105*(a[7]+a[13]); H=105*(a[6]+a[14]);
225 Bp=-3*(ap[2]+ap[4]); Cp=3*(ap[1]+ap[5]); Ep=-2.5*(ap[9]+ap[11]);
226 Fp=7.5*(ap[8]+ap[12]); Gp=-105*(ap[7]+ap[13]); Hp=105*(ap[6]+ap[14]);
229 Bs=-B; Cs=-4*C; Es=-E;
230 Fs=-4*F; Gs=-9*G; Hs=-16*H;
233 std::vector<double> tanTheta = SolveCubic(Hp+Cp-Fp, Gp+Bp-3*Ep, 6*Fp+Cp, Bp+4*Ep);
239 for (
int n=0; n<tanTheta.size(); n++)
241 double tmp = atan(tanTheta[n]);
242 double theta = floor(tmp/
M_PI);
243 theta = tmp - theta*
M_PI;
245 sn = sin(2*theta); cs = cos(2*theta);
246 tmp = ODF_dtheta(sn, cs, A, B, C, D, E, F, G, H);
251 vnl_matrix_fixed< double, 2, 2 > hessian;
252 hessian(0,0) = ODF_dtheta2(sn, cs, A, B, C, D, E, F, G, H);
253 hessian(0,1) = ODF_dtheta(sn, cs, 0, Bp, Cp, 0, Ep, Fp, Gp, Hp);
254 hessian(1,0) = hessian(0,1);
255 hessian(1,1) = ODF_dphi2(sn, cs, 0, Bs, Cs, 0, Es, Fs, Gs, Hs);
257 double det = vnl_det(hessian);
258 double tr = vnl_trace(hessian);
263 vnl_vector_fixed< double, 2 > peak;
266 m_CandidatePeaks.push_back(peak);
270 if (adaptiveStepwidth)
272 double t2=tanTheta[n]*tanTheta[n];
double t3=t2*tanTheta[n];
double t4=t3*tanTheta[n];
273 double const_step=phi_step*(1+t2)/sqrt(t2+t4+pow((((Hs+Cs-Fs)*t3+(Gs+Bs-3*Es)*t2+(6*Fs+Cs)*tanTheta[n]+(Bs+4*Es))/(3*(Hp+Cp-Fp)*t2+2*(Gp+Bp-3*Ep)*tanTheta[n]+(6*Fp+Cp))),2.0));
288 template<
class TOdfPixelType >
292 const double distThres = 0.4;
293 int npeaks = 0, nMin = 0;
294 double dMin, dPos, dNeg, d;
296 vector< Vector3D > v;
299 std::vector < std::vector< Vector3D > > clusters;
300 clusters.resize(m_CandidatePeaks.size());
302 for (
int i=0; i<m_CandidatePeaks.size(); i++)
305 u[0] = sin(m_CandidatePeaks[i](0))*cos(m_CandidatePeaks[i](1));
306 u[1] = sin(m_CandidatePeaks[i](0))*sin(m_CandidatePeaks[i](1));
307 u[2] = cos(m_CandidatePeaks[i](0));
310 for (
int n=0; n<npeaks; n++)
313 dPos = vnl_vector_ssd(v[n],u);
314 dNeg = vnl_vector_ssd(v[n],-u);
325 if ( dMin<distThres )
327 clusters[nMin].push_back(u);
337 for (
int i=0; i<m_CandidatePeaks.size(); i++)
338 if ( !clusters[i].empty() )
341 for (
int vc=0; vc<clusters[i].size(); vc++)
342 v[i]=v[i]+clusters[i][vc];
350 vnl_matrix< double > shBasis, sphCoords;
352 Cart2Sph(v, sphCoords);
353 shBasis = CalcShBasis(sphCoords, 4);
354 vnl_vector<double> odfVals(npeaks);
356 double maxVal = itk::NumericTraits<double>::NonpositiveMin();
358 for (
int i=0; i<npeaks; i++)
360 for (
int j=0; j<15; j++)
361 odfVals(i) += shCoeff[j]*shBasis(i,j);
363 if ( odfVals(i)>maxVal )
370 vector< double > restVals;
371 for (
int i=0; i<npeaks; i++)
372 if ( odfVals(i)>=m_PeakThreshold*maxVal )
374 u[0] = odfVals(i)*cos(sphCoords(i,1))*sin(sphCoords(i,0));
375 u[1] = odfVals(i)*sin(sphCoords(i,1))*sin(sphCoords(i,0));
376 u[2] = odfVals(i)*cos(sphCoords(i,0));
377 restVals.push_back(odfVals(i));
382 if (npeaks>m_MaxNumPeaks)
384 vector< Vector3D > v2;
385 for (
int i=0; i<m_MaxNumPeaks; i++)
387 maxVal = itk::NumericTraits<double>::NonpositiveMin();
388 for (
int i=0; i<npeaks; i++)
389 if ( restVals[i]>maxVal )
391 maxVal = restVals[i];
395 v2.push_back(v[maxPos]);
396 restVals[maxPos] = 0;
405 template<
class TOdfPixelType >
407 ::Cart2Sph(
const std::vector< Vector3D >& dir, vnl_matrix<double>& sphCoords)
409 sphCoords.set_size(dir.size(), 2);
411 for (
int i=0; i<dir.size(); i++)
413 double mag = dir[i].magnitude();
417 sphCoords(i,0) =
M_PI/2;
418 sphCoords(i,1) =
M_PI/2;
422 sphCoords(i,0) = acos(dir[i](2)/mag);
423 sphCoords(i,1) = atan2(dir[i](1), dir[i](0));
429 template<
class TOdfPixelType >
433 int R = (shOrder+1)*(shOrder+2)/2;
434 int M = sphCoords.rows();
435 int j, m;
double mag, plm;
436 vnl_matrix<double> shBasis;
437 shBasis.set_size(M,R);
439 for (
int p=0; p<M; p++)
442 for (
int l=0; l<=shOrder; l=l+2)
443 for (m=-l; m<=l; m++)
445 plm = legendre_p<double>(l,abs(m),cos(sphCoords(p,0)));
446 mag = sqrt((
double)(2*l+1)/(4.0*
M_PI)*factorial<double>(l-abs(m))/factorial<double>(l+abs(m)))*plm;
449 shBasis(p,j) = sqrt(2.0)*mag*cos(fabs((
double)m)*sphCoords(p,1));
453 shBasis(p,j) = pow(-1.0, m)*sqrt(2.0)*mag*sin(m*sphCoords(p,1));
460 template<
class TOdfPixelType >
464 if (!ReconstructQballImage())
467 std::cout <<
"Starting maxima extraction\n";
469 switch (m_NormalizationMethod)
472 std::cout <<
"NO_NORM\n";
474 case SINGLE_VEC_NORM:
475 std::cout <<
"SINGLE_VEC_NORM\n";
478 std::cout <<
"MAX_VEC_NORM\n";
482 typedef ImageRegionConstIterator< CoefficientImageType > InputIteratorType;
485 InputIteratorType git(m_ShCoeffImage, m_ShCoeffImage->GetLargestPossibleRegion() );
487 itk::Vector<double,3> spacing = m_ShCoeffImage->GetSpacing();
488 double minSpacing = spacing[0];
489 if (spacing[1]<minSpacing)
490 minSpacing = spacing[1];
491 if (spacing[2]<minSpacing)
492 minSpacing = spacing[2];
495 itk::Matrix<double, 3, 3> direction = m_ShCoeffImage->GetDirection();
496 ImageRegion<3> imageRegion = m_ShCoeffImage->GetLargestPossibleRegion();
500 m_NumDirectionsImage->SetSpacing( spacing );
501 m_NumDirectionsImage->SetOrigin( origin );
502 m_NumDirectionsImage->SetDirection( direction );
503 m_NumDirectionsImage->SetRegions( imageRegion );
504 m_NumDirectionsImage->Allocate();
505 m_NumDirectionsImage->FillBuffer(0);
511 for (
int i=0; i<m_MaxNumPeaks; i++)
513 itk::Vector< float, 3 > nullVec; nullVec.Fill(0.0);
515 img->SetSpacing( spacing );
516 img->SetOrigin( origin );
517 img->SetDirection( direction );
518 img->SetRegions( imageRegion );
520 img->FillBuffer(nullVec);
521 m_DirectionImageContainer->InsertElement(m_DirectionImageContainer->Size(), img);
524 if (m_MaskImage.IsNull())
527 m_MaskImage->SetSpacing( spacing );
528 m_MaskImage->SetOrigin( origin );
529 m_MaskImage->SetDirection( direction );
530 m_MaskImage->SetRegions( imageRegion );
531 m_MaskImage->Allocate();
532 m_MaskImage->FillBuffer(1);
535 itk::ImageRegionIterator<ItkUcharImgType> dirIt(m_NumDirectionsImage, m_NumDirectionsImage->GetLargestPossibleRegion());
536 itk::ImageRegionIterator<ItkUcharImgType> maskIt(m_MaskImage, m_MaskImage->GetLargestPossibleRegion());
538 int maxProgress = m_MaskImage->GetLargestPossibleRegion().GetSize()[0]*m_MaskImage->GetLargestPossibleRegion().GetSize()[1]*m_MaskImage->GetLargestPossibleRegion().GetSize()[2];
540 boost::progress_display disp(maxProgress);
543 while( !git.IsAtEnd() )
546 if (maskIt.Value()<=0)
555 FindCandidatePeaks(c);
556 std::vector< Vector3D > directions = ClusterPeaks(c);
558 typename CoefficientImageType::IndexType index = git.GetIndex();
561 for (
int i=0; i<directions.size(); i++)
562 if (directions.at(i).magnitude()>
max)
563 max = directions.at(i).magnitude();
567 for (
int i=0; i<directions.size(); i++)
570 itk::Vector< float, 3 > pixel;
571 vnl_vector<double> dir = directions.at(i);
574 itk::ContinuousIndex<double, 3> center;
575 center[0] = index[0];
576 center[1] = index[1];
577 center[2] = index[2];
578 itk::Point<double> worldCenter;
579 m_ShCoeffImage->TransformContinuousIndexToPhysicalPoint( center, worldCenter );
581 switch (m_NormalizationMethod)
585 case SINGLE_VEC_NORM:
593 dir = m_MaskImage->GetDirection()*dir;
594 pixel.SetElement(0, dir[0]);
595 pixel.SetElement(1, dir[1]);
596 pixel.SetElement(2, dir[2]);
597 img->SetPixel(index, pixel);
599 itk::Point<double> worldStart;
600 worldStart[0] = worldCenter[0]-dir[0]/2 * minSpacing;
601 worldStart[1] = worldCenter[1]-dir[1]/2 * minSpacing;
602 worldStart[2] = worldCenter[2]-dir[2]/2 * minSpacing;
603 vtkIdType
id = m_VtkPoints->InsertNextPoint(worldStart.GetDataPointer());
604 container->GetPointIds()->InsertNextId(
id);
605 itk::Point<double> worldEnd;
606 worldEnd[0] = worldCenter[0]+dir[0]/2 * minSpacing;
607 worldEnd[1] = worldCenter[1]+dir[1]/2 * minSpacing;
608 worldEnd[2] = worldCenter[2]+dir[2]/2 * minSpacing;
609 id = m_VtkPoints->InsertNextPoint(worldEnd.GetDataPointer());
610 container->GetPointIds()->InsertNextId(
id);
611 m_VtkCellArray->InsertNextCell(container);
614 dirIt.Set(directions.size());
622 directionsPolyData->SetPoints(m_VtkPoints);
623 directionsPolyData->SetLines(m_VtkCellArray);
625 std::cout <<
"Maxima extraction finished\n";
629 #endif // __itkOdfMaximaExtractionFilter_cpp
itk::SmartPointer< Self > Pointer
MITKCORE_EXPORT const ScalarType eps
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.