ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
NormalDistribution.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #ifdef _MSC_VER
9 // To get rid of the really annoying warnings about unsafe methods
10 #pragma warning(disable : 4996)
11 #endif
12 
13 #include <NormalDistribution.h>
14 
15 // local
17 #include <ErrorFunction.h>
18 #include <GenericCloud.h>
19 #include <ScalarField.h>
20 #include <ScalarFieldTools.h>
21 
22 using namespace cloudViewer;
23 
26  m_mu(0),
27  m_sigma2(0),
28  m_qFactor(0),
29  m_normFactor(0) {}
30 
31 NormalDistribution::NormalDistribution(ScalarType mu, ScalarType sigma2) {
32  setParameters(mu, sigma2);
33 }
34 
36  ScalarType& sigma2) const {
37  mu = m_mu;
38  sigma2 = m_sigma2;
39 
40  return isValid();
41 }
42 
43 bool NormalDistribution::setParameters(ScalarType mu, ScalarType sigma2) {
44  m_mu = mu;
45  m_sigma2 = sigma2;
46 
47  // update Chi2 data
48  m_chi2ClassesPositions.resize(0);
49  m_Pi.resize(0);
50 
51  if (m_sigma2 >= 0) {
52  setValid(true);
53  m_qFactor = 1.0 / (2.0 * m_sigma2);
54  m_normFactor = 1.0 / sqrt(2.0 * M_PI * m_sigma2);
55  } else {
56  setValid(false);
57  m_qFactor = 1.0;
58  m_normFactor = 1.0;
59  }
60 
61  return isValid();
62 }
63 
64 double NormalDistribution::computeP(ScalarType x) const {
65  double p = static_cast<double>(x - m_mu);
66  return exp(-p * p * m_qFactor) * m_normFactor;
67 }
68 
69 double NormalDistribution::computeP(ScalarType x1, ScalarType x2) const {
70  return 0.5 * (ErrorFunction::erf(static_cast<double>(x2 - m_mu) /
71  sqrt(2 * m_sigma2)) -
72  ErrorFunction::erf(static_cast<double>(x1 - m_mu) /
73  sqrt(2 * m_sigma2)));
74 }
75 
76 double NormalDistribution::computePfromZero(ScalarType x) const {
77  return 0.5 * (ErrorFunction::erf(static_cast<double>(x - m_mu) /
78  sqrt(2 * m_sigma2)) +
79  1.0);
80 }
81 
83  setValid(false);
84 
85  double mean = 0.0, stddev2 = 0.0;
86  unsigned counter = 0;
87 
88  unsigned n = cloud->size();
89  for (unsigned i = 0; i < n; ++i) {
90  ScalarType v = cloud->getPointScalarValue(i);
91  if (ScalarField::ValidValue(v)) {
92  mean += v;
93  stddev2 += static_cast<double>(v) * v;
94  ++counter;
95  }
96  }
97 
98  if (counter == 0) {
99  return false;
100  }
101 
102  mean /= counter;
103  stddev2 = std::abs(stddev2 / counter - mean * mean);
104 
105  return setParameters(static_cast<ScalarType>(mean),
106  static_cast<ScalarType>(stddev2));
107 }
108 
110  setValid(false);
111 
112  // compute mean and std. dev.
113  double mean = 0.0, stddev2 = 0.0;
114  unsigned counter = 0;
115 
116  for (ScalarType v : values) {
117  if (ScalarField::ValidValue(v)) {
118  mean += v;
119  stddev2 += static_cast<double>(v) * v;
120  ++counter;
121  }
122  }
123 
124  if (counter == 0) {
125  return false;
126  }
127 
128  mean /= counter;
129  stddev2 = std::abs(stddev2 / counter - mean * mean);
130 
131  return setParameters(static_cast<ScalarType>(mean),
132  static_cast<ScalarType>(stddev2));
133 }
134 
136  double nSigma) {
137  if (!computeParameters(values)) return false;
138 
139  // max std. deviation
140  const double maxStddev = sqrt(static_cast<double>(m_sigma2)) * nSigma;
141 
142  unsigned counter = 0;
143  double mean = 0.0, stddev2 = 0.0;
144 
145  for (ScalarType v : values) {
146  if (static_cast<double>(std::abs(v - m_mu)) < maxStddev) {
147  mean += v;
148  stddev2 += static_cast<double>(v) * v;
149  ++counter;
150  }
151  }
152 
153  if (counter == 0) {
154  return false;
155  }
156 
157  mean /= counter;
158  stddev2 = std::abs(stddev2 / counter - mean * mean);
159 
160  return setParameters(static_cast<ScalarType>(mean),
161  static_cast<ScalarType>(stddev2));
162 }
163 
165  unsigned numberOfClasses,
166  int* histo) {
167  assert(cloud);
168 
169  unsigned n = cloud->size();
170 
171  // we must refine the real number of elements
172  unsigned numberOfElements =
174 
175  if (numberOfElements == 0) return -1.0;
176 
177  if (numberOfClasses < 1 ||
178  numberOfClasses * numberOfClasses > numberOfElements)
179  return -1.0;
180  else if (numberOfClasses == 1)
181  return 0.0;
182 
183  if (!setChi2ClassesPositions(numberOfClasses)) return -1.0;
184 
185  assert(m_Pi.size() == numberOfClasses);
186 
187  int* _histo = histo;
188  if (!_histo) _histo = new int[numberOfClasses];
189  if (!_histo) return -1.0;
190 
191  memset(_histo, 0, numberOfClasses * sizeof(int));
192 
193  // histogram computation
194  for (unsigned i = 0; i < n; ++i) {
195  ScalarType V = cloud->getPointScalarValue(i);
196  if (ScalarField::ValidValue(V)) {
197  unsigned j = 0;
198  for (; j < numberOfClasses - 1; ++j)
199  if (V < m_chi2ClassesPositions[j]) break;
200 
201  ++_histo[j];
202  }
203  }
204 
205  // calcul de la distance du Chi2
206  double dk = 0.0;
207  {
208  for (unsigned i = 0; i < numberOfClasses; ++i) {
209  double nPi = static_cast<double>(m_Pi[i]) * numberOfElements;
210  double tempValue = static_cast<double>(_histo[i]) - nPi;
211  dk += tempValue * tempValue / nPi;
212  }
213  }
214 
215  if (_histo && !histo) delete[] _histo;
216  _histo = nullptr;
217 
218  return dk;
219 }
220 
221 bool NormalDistribution::setChi2ClassesPositions(unsigned numberOfClasses) {
222  m_chi2ClassesPositions.resize(0);
223  m_Pi.resize(0);
224 
225  if (!isValid() || numberOfClasses < 2) return false;
226 
227  try {
228  m_Pi.reserve(numberOfClasses);
229  m_chi2ClassesPositions.reserve(numberOfClasses - 1);
230  } catch (const std::bad_alloc&) {
231  // not engouh memory
232  return false;
233  }
234 
235  // simplest case
236  if (numberOfClasses == 2) {
237  m_Pi.push_back(0.5);
238  m_chi2ClassesPositions.push_back(m_mu);
239  m_Pi.push_back(0.5);
240  } else // general case: numberOfClasses>2
241  {
242  ScalarType sigma = sqrt(m_sigma2);
243  // 1st class between -inf and mu-2.sigma
244  ScalarType x = m_mu - 2 * sigma;
245  ScalarType y = static_cast<ScalarType>(computePfromZero(x));
246  m_Pi.push_back(y);
247  m_chi2ClassesPositions.push_back(x);
248 
249  // numberOfClasses-2 classes between mu-2.sigma and mu+2.sigma
250  ScalarType pas = 4 * sigma / (numberOfClasses - 2);
251  for (unsigned i = 0; i < numberOfClasses - 2; ++i) {
252  x = x + pas;
253  ScalarType oldy = y;
254  y = static_cast<ScalarType>(computePfromZero(x));
255  m_Pi.push_back(y - oldy);
256  m_chi2ClassesPositions.push_back(x);
257  }
258 
259  // last class between mu+2.sigma and +inf
260  // x = m_mu + 2 * sigma;
261  y = 1 - y;
262  m_Pi.push_back(y);
263  }
264 
265  return true;
266 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
static double erf(double x)
Computes erf(x)
virtual unsigned size() const =0
Returns the number of points.
virtual ScalarType getPointScalarValue(unsigned pointIndex) const =0
Returns the ith point associated scalar value.
A generic class to handle a probability distribution.
virtual bool isValid() const
Indicates if the distribution parameters are valid.
void setValid(bool state)
Sets distribution current validity.
std::vector< ScalarType > ScalarContainer
Scalar values container.
double computeChi2Dist(const GenericCloud *Yk, unsigned numberOfClasses, int *histo=nullptr) override
Computes the Chi2 distance (related to the Chi2 Test)
bool computeRobustParameters(const ScalarContainer &values, double nSigma)
bool setParameters(ScalarType _mu, ScalarType _sigma2)
Sets the distribution parameters.
NormalDistribution()
NormalDistribution constructor.
bool computeParameters(const ScalarContainer &values) override
Computes the distribution parameters from a set of values.
bool getParameters(ScalarType &_mu, ScalarType &_sigma2) const
Returns the distribution parameters.
double m_qFactor
Exponential quotient.
double m_normFactor
Normalization factor.
virtual bool setChi2ClassesPositions(unsigned numberOfClasses)
Compute each Chi2 class limits.
std::vector< ScalarType > m_chi2ClassesPositions
Chi2 classes limits.
std::vector< ScalarType > m_Pi
Structure used during the Chi2 distance computation.
double computePfromZero(ScalarType x) const override
Computes the cumulative probability between 0 and x.
double computeP(ScalarType x) const override
Computes the probability of x.
static unsigned countScalarFieldValidValues(const GenericCloud *theCloud)
Count the number of valid values in a scalar field.
static bool ValidValue(ScalarType value)
Returns whether a scalar value is valid or not.
Definition: ScalarField.h:61
__host__ __device__ int2 abs(int2 v)
Definition: cutil_math.h:1267
Generic file read and write utility for python interface.