Direct Graphical Models  v.1.7.0
KDGauss.cpp
1 #include "KDGauss.h"
2 #include "random.h"
3 #include "mathop.h"
4 #include "macroses.h"
5 
6 namespace DirectGraphicalModels
7 {
8  // Constants
9  const bool CKDGauss::USE_SAFE_SIGMA = false;
10 
11  // Constructor
12  CKDGauss::CKDGauss(dword k) {
13  m_nPoints = 0;
14  m_mu = Mat::zeros(k, 1, CV_64FC1);
15  m_sigma = USE_SAFE_SIGMA ? Mat::eye(k, k, CV_64FC1) : Mat::zeros(k, k, CV_64FC1);
16  }
17 
18  // Constructor
19  CKDGauss::CKDGauss(const Mat &mu) {
20  m_nPoints = 1;
21  mu.convertTo(m_mu, CV_64FC1);
22  int k = mu.rows;
23  m_sigma = USE_SAFE_SIGMA ? Mat::eye(k, k, CV_64FC1) : Mat::zeros(k, k, CV_64FC1);
24  }
25 
26  // Copy Constructor
28  this->m_nPoints = rhs.m_nPoints;
29  this->m_mu = rhs.m_mu.clone();
30  this->m_sigma = rhs.m_sigma.clone();
31  this->m_sigmaInv = rhs.m_sigmaInv.empty() ? Mat() : rhs.m_sigmaInv.clone();
32  this->m_Q = rhs.m_Q.empty() ? Mat() : rhs.m_Q.clone();
33  this->m_alpha = rhs.m_alpha;
34  }
35 
36  // Copy Operator
38  if (this != &rhs) {
39  this->m_nPoints = rhs.m_nPoints;
40  this->m_mu = rhs.m_mu;
41  this->m_sigma = rhs.m_sigma;
42  this->m_sigmaInv = rhs.m_sigmaInv.empty() ? Mat() : rhs.m_sigmaInv.clone();
43  this->m_Q = rhs.m_Q.empty() ? Mat() : rhs.m_Q.clone();
44  this->m_alpha = rhs.m_alpha;
45  }
46  return *this;
47  }
48 
49  // Compound Plus Operator
51  if (rhs.m_nPoints == 1) this->addPoint(rhs.m_mu);
52  else {
53  long double a = static_cast<long double>(this->m_nPoints) / (this->m_nPoints + rhs.m_nPoints);
54  this->m_nPoints += rhs.m_nPoints;
55 
56  // fast matrix multiplication (faster than OpenCV gemm())
57  // sigma^ = a * (sigma1 + mu1 * mu1^T) + (1 - a) * (sigma2 + mu2 * mu2^T)
58  for (int y = 0; y < this->m_sigma.rows; y++) {
59  double *pSigma = this->m_sigma.ptr<double>(y);
60  const double *pRhsSigma = rhs.m_sigma.ptr<double>(y);
61  for (int x = 0; x < this->m_sigma.cols; x++) {
62  pSigma[x] = static_cast<double>( a * (pSigma[x] + this->m_mu.at<double>(x, 0) * this->m_mu.at<double>(y, 0)) +
63  (1.0 - a) * (pRhsSigma[x] + rhs.m_mu.at<double>(x, 0) * rhs.m_mu.at<double>(y, 0)));
64  } // x
65  } // y
66 
67  addWeighted(this->m_mu, a, rhs.m_mu, 1.0 - a, 0.0, this->m_mu); // mu^ = a * mu1 + (1 - a) * mu2
68  this->m_sigma -= this->m_mu * this->m_mu.t(); // sigma^ = a * (sigma1 + mu1 * mu1^t) + (1 - a) * (sigma2 + mu2 * mu2^t) - mu^ * mu^^T
70  }
71  return *this;
72  }
73 
74  // Compound Plus Operator
75  CKDGauss & CKDGauss::operator+= (const Mat &point) {
76  // return this->operator+=(CKDGauss(point));
77  addPoint(point);
78  return *this;
79  }
80 
81  void CKDGauss::clear(void)
82  {
83  m_nPoints = 0;
84  m_mu.setTo(0);
85  int k = m_mu.rows;
86  m_sigma = USE_SAFE_SIGMA ? Mat::eye(k, k, CV_64FC1) : Mat::zeros(k, k, CV_64FC1);
88  }
89 
90  void CKDGauss::addPoint(const Mat &point, bool approximate)
91  {
92  // Assertions
93  DGM_ASSERT_MSG(point.size() == m_mu.size(), "Wrong input point size");
94  DGM_ASSERT_MSG(point.type() == m_mu.type(), "Wrong input point type");
95 
96  if (m_nPoints == 0) point.copyTo(m_mu);
97  else {
98  long double a = static_cast<long double>(m_nPoints) / (m_nPoints + 1);
99 
100  if (approximate) { // ---------------- approximate calculation of sigma ----------------
101  addWeighted(m_mu, a, point, (1.0 - a), 0.0, m_mu); // mu = a * mu + (1-a) * point
102 
103  // fast matrix multiplation (faster than OpenCV gemm())
104  // sigma^ = a * sigma1 + (1 - a) * (point * point^t - mu1 * mu1^T)
105  for (int y = 0; y < m_sigma.rows; y++) {
106  double *pSigma = m_sigma.ptr<double>(y);
107  for (int x = 0; x < m_sigma.cols; x++) {
108  double cr = (point.at<double>(x, 0) - m_mu.at<double>(x, 0)) * (point.at<double>(y, 0) - m_mu.at<double>(y, 0));
109  pSigma[x] = cr + static_cast<double>(a * (pSigma[x] - cr));
110  } // x
111  } // y
112  }
113  else { // ---------------- general exact calculation of sigma ----------------
114  Mat mu;
115  addWeighted(this->m_mu, a, point, 1.0 - a, 0.0, mu); // mu^ = a * mu1 + (1- a) * point
116 
117  // fast matrix multiplation (faster than OpenCV gemm())
118  // sigma^ = a * (sigma1 + mu1 * mu1^t) + (1 - a) * point * point^t - mu^ * mu^^T
119  for (int y = 0; y < m_sigma.rows; y++) {
120  double *pSigma = m_sigma.ptr<double>(y);
121  for (int x = 0; x < m_sigma.cols; x++) {
122  pSigma[x] = static_cast<double>(a * (pSigma[x] + m_mu.at<double>(x, 0) * m_mu.at<double>(y, 0)) +
123  (1.0 - a) * point.at<double>(x, 0) * point.at<double>(y, 0) -
124  mu.at<double>(x, 0) * mu.at<double>(y, 0));
125  } // x
126  } // y
127  this->m_mu = mu;
128  }
129  }
130  m_nPoints++;
132  }
133 
134  void CKDGauss::setMu(Mat &mu)
135  {
136  // Assertions
137  DGM_ASSERT_MSG(mu.size() == m_mu.size(), "Wrong mu size");
138  DGM_ASSERT_MSG(mu.type() == m_mu.type(), "Wrong mu type");
139 
140  mu.copyTo(m_mu);
141  m_nPoints = 1;
143  }
144 
145  void CKDGauss::setSigma(Mat &sigma)
146  {
147  // Assertions
148  DGM_ASSERT_MSG(sigma.size() == m_sigma.size(), "Wrong sigma size");
149  DGM_ASSERT_MSG(sigma.type() == m_sigma.type(), "Wrong sigma type");
150 
151  sigma.copyTo(m_sigma);
152  m_nPoints = 1;
154  }
155 
156  Mat CKDGauss::getSigmaInv(void) const
157  {
158  if (m_sigmaInv.empty()) invert(m_sigma, m_sigmaInv, DECOMP_SVD); // sigmaInv = sigma^-1
159 
160  return m_sigmaInv;
161  }
162 
163  long double CKDGauss::getAlpha(void) const
164  {
165  if (m_alpha < 0) {
166  int k = m_sigma.cols;
167  long double det = MAX(LDBL_EPSILON, sqrtl(static_cast<long double>(determinant(m_sigma))));
168  long double sPi = powl(2 * static_cast<long double>(Pi), static_cast<long double>(k) / 2);
169  m_alpha = 1 / (det * sPi);
170  }
171 
172  return m_alpha;
173  }
174 
175  double CKDGauss::getValue(Mat &x, Mat &X, Mat &p1, Mat &p2) const
176  {
177  // Assertions
178  DGM_ASSERT_MSG(x.size() == m_mu.size(), "Wrong x size");
179  DGM_ASSERT_MSG(x.type() == m_mu.type(), "Wrong x type");
180 
181  subtract(x, m_mu, X); // X = x - mu
182  gemm(getSigmaInv(), X, 1.0, Mat(), 0.0, p1, 0);
183  gemm(X, p1, 1.0, Mat(), 0.0, p2, GEMM_1_T);
184  double value = -0.5 * p2.at<double>(0, 0); // val = -0.5 * (X-mu)^T * Sigma^-1 * (X-mu)
185 
186  return exp(value);
187  }
188 
189  double CKDGauss::getEuclidianDistance(const Mat &x) const
190  {
191  // Assertions (mathop::Euclidian also checks that)
192  DGM_ASSERT_MSG(x.size() == m_mu.size(), "Wrong x size");
193  DGM_ASSERT_MSG(x.type() == m_mu.type(), "Wrong x type");
194 
195  return mathop::Euclidian<double, double>(m_mu, x);
196  }
197 
198  double CKDGauss::getMahalanobisDistance(const Mat &x) const
199  {
200  // Assertions
201  DGM_ASSERT_MSG(x.size() == m_mu.size(), "Wrong x size");
202  DGM_ASSERT_MSG(x.type() == m_mu.type(), "Wrong x type");
203 
204  return static_cast<double>(Mahalanobis(x, m_mu, getSigmaInv()));
205  }
206 
208  {
209  // Assertions
210  DGM_ASSERT_MSG(x.getMu().size() == m_mu.size(), "Wrong x.mu size");
211  DGM_ASSERT_MSG(x.getMu().type() == m_mu.type(), "Wrong x.mu type");
212 
213  Mat p;
214  gemm(x.getSigmaInv(), m_sigma, 1.0, Mat(), 0.0, p, 0); // p = \Sigma^{-1}_{x} * \Sigma
215  cv::Scalar tr = trace(p);
216  p.release();
217 
218  double dst = x.getMahalanobisDistance(m_mu);
219 
220  int k = m_mu.rows;
221 
222  double ln = log(determinant(m_sigma) / determinant(x.getSigma()));
223 
224  double res = static_cast<double>(tr.val[0] + dst*dst - k - ln) / 2;
225 
226  return res;
227  }
228 
229  Mat CKDGauss::getSample(void) const
230  {
231  Mat X = random::N(m_mu.size(), m_mu.type()); // X - vector of independ random variable with normal distribution
232 
233  if (m_Q.empty()) m_Q = calculateQ();
234 
235  Mat res;
236  gemm(m_Q, X, 1, m_mu, 1, res, GEMM_1_T);
237  return res;
238  }
239 
240  // ---------------------- Private functions ----------------------
241 
243  {
244  if (!m_sigmaInv.empty()) m_sigmaInv.release();
245  if (!m_Q.empty()) m_Q.release();
246  m_alpha = -1.0;
247  }
248 
249  Mat CKDGauss::calculateQ(void) const
250  {
251  int D = m_mu.rows; // dimension
252 
253  // eigenvalues and eigenvectors of <sigma>
254  Mat E, F;
255  eigen(m_sigma, E, F);
256 
257  // Diagonal Matrix L
258  Mat L(D, D, CV_64FC1); L.setTo(0);
259  for (int d = 0; d < D; d++) L.at<double>(d, d) = E.at<double>(d, 0);
260  sqrt(L, L);
261 
262  // Matrix Q
263  Mat res;
264  gemm(L, F, 1, Mat(), 0, res);
265 
266  return res;
267  }
268 }
void clear(void)
Clears class variables.
Definition: KDGauss.cpp:81
void addPoint(const Mat &point, bool approximate=false)
Adds new k-dimensional point (sample) for Gaussian distribution approximation.
Definition: KDGauss.cpp:90
Mat getSigmaInv(void) const
Returns .
Definition: KDGauss.cpp:156
double getMahalanobisDistance(const Mat &x) const
Returns the Mahalanobis distance between argument point x and the center of multivariate normal distr...
Definition: KDGauss.cpp:198
void setSigma(Mat &sigma)
Sets .
Definition: KDGauss.cpp:145
Mat getSample(void) const
Returns a random vector (sample) from multivariate normal distribution.
Definition: KDGauss.cpp:229
double getValue(Mat &x, Mat &aux1=EmptyMat, Mat &aux2=EmptyMat, Mat &aux3=EmptyMat) const
Returns unscaled value of the Gaussian function.
Definition: KDGauss.cpp:175
CKDGauss & operator+=(const CKDGauss &rhs)
Compound merge operator.
Definition: KDGauss.cpp:50
long double getAlpha(void) const
Returns .
Definition: KDGauss.cpp:163
CKDGauss(dword k)
Constructor.
Definition: KDGauss.cpp:12
double getKullbackLeiberDivergence(const CKDGauss &x) const
Returns the Kullback-Leiber divergence from the multivariate normal distribution to argument multiva...
Definition: KDGauss.cpp:207
double getEuclidianDistance(const Mat &x) const
Returns the Euclidian distance between argument point x and the center of multivariate normal distrib...
Definition: KDGauss.cpp:189
Mat getSigma(void) const
Returns .
Definition: KDGauss.h:119
void setMu(Mat &mu)
Sets .
Definition: KDGauss.cpp:134
CKDGauss & operator=(const CKDGauss &rhs)
Copy operator.
Definition: KDGauss.cpp:37
Multivariate Gaussian distribution class.
Definition: KDGauss.h:28
Mat getMu(void) const
Returns .
Definition: KDGauss.h:109
static const bool USE_SAFE_SIGMA
Definition: KDGauss.h:207
T N(T mu=0, T sigma=1)
Returns a floating-point random number with normal distribution.
Definition: random.h:63