Shapeworks Studio  2.1
Shape analysis software suite
itkPSMParticleEntropyFunction.hxx
1 /*=========================================================================
2  *
3  * Copyright Insight Software Consortium
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *=========================================================================*/
18 #ifndef __itkPSMParticleEntropyFunction_hxx
19 #define __itkPSMParticleEntropyFunction_hxx
20 #include "itkPSMParticleEntropyFunction.h"
21 
22 namespace itk {
23 
24 template <class TGradientNumericType, unsigned int VDimension>
25 TGradientNumericType
27 ::AngleCoefficient( const GradientVectorType& p_i_normal, const GradientVectorType& p_j_normal) const
28 {
29  // Get the cosine of the angle between the two particles' normals
30  TGradientNumericType cosine = dot_product(p_i_normal,p_j_normal) /
31  (p_i_normal.magnitude()*p_j_normal.magnitude() + 1.0e-6);
32 
33  // the flat region
34  if ( cosine >= m_FlatCutoff ) return 1.0;
35 
36  // the feathered region
37  return ( cos( (m_FlatCutoff - cosine) / (1.0+m_FlatCutoff) * (3.14159265358979/2.0) )) ;
38 }
39 
40 template <class TGradientNumericType, unsigned int VDimension>
41 void
44  const typename ParticleSystemType::PointVectorType &neighborhood,
46  std::vector<double> &weights) const
47 {
48  GradientVectorType posnormal = domain->SampleNormalVnl(pos, 1.0e-10);
49  weights.resize(neighborhood.size());
50 
51  for (unsigned int i = 0; i < neighborhood.size(); i++)
52  {
53  weights[i] = this->AngleCoefficient(posnormal,
54  domain->SampleNormalVnl(neighborhood[i].Point, 1.0e-10));
55  if (weights[i] < 1.0e-5) weights[i] = 0.0;
56  }
57 }
58 
59 template <class TGradientNumericType, unsigned int VDimension>
60 double
62 ::EstimateSigma(unsigned int, const typename ParticleSystemType::PointVectorType &neighborhood,
63  const std::vector<double> &weights,
64  const PointType &pos, double initial_sigma, double precision,
65  int &err) const
66 {
67  const double epsilon = 1.0e-5;
68  const double min_sigma = 1.0e-4;
69 
70  const double M = static_cast<double>(VDimension);
71  const double MM = M * M * 2.0 + M;
72 
73  double error = 1.0e6;
74  double sigma, prev_sigma;
75  sigma = initial_sigma;
76 
77  while (error > precision)
78  {
79  VectorType r_vec;
80  double A = 0.0;
81  double B = 0.0;
82  double C = 0.0;
83  double sigma2 = sigma * sigma;
84  double sigma22 = sigma2 * 2.0;
85 
86  for (unsigned int i = 0; i < neighborhood.size(); i++)
87  {
88  if (weights[i] < epsilon) continue;
89 
90  // if ( neighborhood[i].Index == idx) continue;
91  for (unsigned int n = 0; n < VDimension; n++)
92  {
93  r_vec[n] = pos[n] - neighborhood[i].Point[n];
94  }
95 
96  double r = r_vec.magnitude();
97  double r2 = r*r;
98  double alpha = exp(-r2 / sigma22) * weights[i];
99  A += alpha;
100  B += r2 * alpha;
101  C += r2 * r2 * alpha;
102  } // end for i
103 
104  prev_sigma = sigma;
105 
106  if (A < epsilon)
107  {
108  err = 1;
109  return sigma;
110  }; // results are not meaningful
111 
112  // First order convergence update. This is a fixed point iteration.
113  //sigma = sqrt(( 1.0 / DIM ) * ( B / A));
114 
115  // Second order convergence update (Newton-Raphson). This is the first
116  // derivative of the negative of the probability density estimation function squared over the
117  // second derivative.
118 
119  // old math
120  // sigma -= (sigma2 * VDimension * A * A - A * B) / (((2.0 * sigma * VDimension) * A * A -
121  // (1.0/(sigma2*sigma))*(A*C-B*B)) + epsilon);
122 
123  // New math -- results are not obviously different?
124  sigma -= (A * (B - A * sigma2 * M)) /
125  ( (-MM * A *A * sigma) - 3.0 * A * B * (1.0 / (sigma + epsilon))
126  - (A*C + B*B) * (1.0 / (sigma2 * sigma + epsilon)) + epsilon);
127 
128  error = 1.0 - fabs((sigma/prev_sigma));
129 
130  // Constrain sigma.
131  if (sigma < min_sigma)
132  {
133  sigma = min_sigma;
134  error = precision; // we are done if sigma has vanished
135  }
136  else
137  {
138  if (sigma < 0.0) sigma = min_sigma;
139  }
140 
141  } // end while (error > precision)
142 
143  err = 0;
144  return sigma;
145 
146 } // end estimate sigma
147 
148 
149 template <class TGradientNumericType, unsigned int VDimension>
152 ::Evaluate(unsigned int idx,unsigned int d, const ParticleSystemType * system,
153  double &maxdt) const
154 {
155  // Grab a pointer to the domain. We need a Domain that has surface normal information.
157  domain = static_cast<const PSMImageDomainWithGradients<
158  TGradientNumericType, VDimension> *>(system->GetDomain(d));
159  const double epsilon = 1.0e-6;
160 
161  // Retrieve the previous optimal sigma value for this point. If the value is
162  // tiny (i.e. unitialized) then use a fraction of the maximum allowed
163  // neighborhood radius.
164  double sigma = m_SpatialSigmaCache->operator[](d)->operator[](idx);
165  if (sigma < epsilon)
166  { sigma = m_MinimumNeighborhoodRadius / m_NeighborhoodToSigmaRatio;}
167 
168  // Determine the extent of the neighborhood that will be used in the Parzen
169  // windowing estimation. The neighborhood extent is based on the optimal
170  // sigma calculation and limited to a user supplied maximum radius (probably
171  // the size of the domain).
172  double neighborhood_radius = sigma * m_NeighborhoodToSigmaRatio;
173  if (neighborhood_radius > m_MaximumNeighborhoodRadius)
174  { neighborhood_radius = m_MaximumNeighborhoodRadius; }
175 
176  // Get the position for which we are computing the gradient.
177  PointType pos = system->GetPosition(idx, d);
178 
179  // Get the neighborhood surrounding the point "pos".
180  typename ParticleSystemType::PointVectorType neighborhood
181  = system->FindNeighborhoodPoints(pos, neighborhood_radius, d);
182 
183  // Compute the weights based on angle between the neighbors and the center.
184  std::vector<double> weights;
185  this->ComputeAngularWeights(pos,neighborhood,domain,weights);
186 
187  // Estimate the best sigma for Parzen windowing. In some cases, such as when
188  // the neighborhood does not include enough points, the value will be bogus.
189  // In these cases, an error != 0 is returned, and we try the estimation again
190  // with an increased neighborhood radius.
191  int err;
192  sigma = this->EstimateSigma(idx, neighborhood,weights,pos, sigma, epsilon, err);
193 
194  while (err != 0)
195  {
196  neighborhood_radius *= 2.0;
197  // Constrain the neighborhood size. If we have reached a maximum
198  // possible neighborhood size, we'll just go with that.
199  if ( neighborhood_radius > this->GetMaximumNeighborhoodRadius())
200  {
201  sigma = this->GetMaximumNeighborhoodRadius() / this->GetNeighborhoodToSigmaRatio();
202  neighborhood_radius = this->GetMaximumNeighborhoodRadius();
203  break;
204  }
205  else
206  {
207  sigma = neighborhood_radius / this->GetNeighborhoodToSigmaRatio();
208  }
209 
210  neighborhood = system->FindNeighborhoodPoints(pos, neighborhood_radius, d);
211  this->ComputeAngularWeights(pos,neighborhood,domain,weights);
212  sigma = this->EstimateSigma(idx, neighborhood, weights, pos, sigma, epsilon, err);
213  } // done while err
214 
215  // Constrain sigma to a maximum reasonable size based on the user-supplied
216  // limit to neighborhood size.
217  if (sigma > this->GetMaximumNeighborhoodRadius())
218  {
219  sigma = this->GetMaximumNeighborhoodRadius() / this->GetNeighborhoodToSigmaRatio();
220  neighborhood_radius = this->GetMaximumNeighborhoodRadius();
221  neighborhood = system->FindNeighborhoodPoints(pos, neighborhood_radius, d);
222  this->ComputeAngularWeights(pos,neighborhood,domain,weights);
223  }
224 
225  // std::cout << idx << "\t SIGMA = " << sigma << "\t NEIGHBORHOOD SIZE = " << neighborhood.size()
226  // << "\t NEIGHBORHOOD RADIUS= " << neighborhood_radius << std::endl;
227 
228  // We are done with the sigma estimation step. Cache the sigma value for
229  // next time.
230  m_SpatialSigmaCache->operator[](d)->operator[](idx) = sigma;
231 
232  //----------------------------------------------
233 
234  // Compute the gradients.
235  double sigma2inv = 1.0 / (2.0* sigma * sigma + epsilon);
236 
237  VectorType r;
238  VectorType gradE;
239 
240  for (unsigned int n = 0; n < VDimension; n++)
241  {
242  gradE[n] = 0.0;
243  }
244 
245  double A = 0.0;
246  for (unsigned int i = 0; i < neighborhood.size(); i++)
247  {
248  // if ( neighborhood[i].Index == idx) continue;
249  if (weights[i] < epsilon) continue;
250 
251  for (unsigned int n = 0; n < VDimension; n++)
252  {
253  // Note that the Neighborhood object has already filtered the
254  // neighborhood for points whose normals differ by > 90 degrees.
255  r[n] = pos[n] - neighborhood[i].Point[n];
256  }
257 
258  double q = exp( -dot_product(r, r) * sigma2inv);
259  A += q;
260 
261  for (unsigned int n = 0; n < VDimension; n++)
262  {
263  gradE[n] += weights[i] * r[n] * q;
264  }
265  }
266 
267  double p = 0.0;
268  if (A > epsilon)
269  {
270  p = -1.0 / (A * sigma * sigma);
271  }
272 
273 
274 
275  // TEST
276  // vnl_vector_fixed<float, VDimension> tosurf = domain->SampleGradientVnl(pos);
277  // float tosurfmag = tosurf.magnitude() + 1.0e-5;
278 
279  // end test
280  // float f = domain->Sample(pos);
281 
282  for (unsigned int n = 0; n <VDimension; n++)
283  {
284  gradE[n] *= p;
285  // TEST
286  // gradE[n] += f * (tosurf[n] / tosurfmag);
287  // end test
288  }
289  // maxdt = sigma * sigma;
290  maxdt = 0.5;
291 
292  return gradE;
293 }
294 
295 
296 }// end namespace
297 #endif
virtual VectorType Evaluate(unsigned int, unsigned int, const ParticleSystemType *, double &) const
PointType & GetPosition(unsigned long int k, unsigned int d=0)
void ComputeAngularWeights(const PointType &, const typename ParticleSystemType::PointVectorType &, const PSMImageDomainWithGradients< TGradientNumericType, VDimension > *, std::vector< double > &) const
A facade class that manages interactions with a particle system.
DomainType * GetDomain(unsigned int i)
TGradientNumericType AngleCoefficient(const GradientVectorType &, const GradientVectorType &) const
virtual double EstimateSigma(unsigned int, const typename ParticleSystemType::PointVectorType &, const std::vector< double > &, const PointType &, double, double, int &err) const
PointVectorType FindNeighborhoodPoints(const PointType &p, double r, unsigned int d=0) const
vnl_vector_fixed< double, VDimension > VectorType
Definition: Shape.h:14