Xmipp  v3.23.11-Nereus
point.cpp
Go to the documentation of this file.
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "cif++/point.hpp"
28 
29 #include <cstdint>
30 #include <cassert>
31 #include <random>
32 
33 namespace cif
34 {
35 
36 // --------------------------------------------------------------------
37 // We're using expression templates here
38 
39 template <typename M>
41 {
42  public:
43  uint32_t dim_m() const { return static_cast<const M &>(*this).dim_m(); }
44  uint32_t dim_n() const { return static_cast<const M &>(*this).dim_n(); }
45 
46  double &operator()(uint32_t i, uint32_t j)
47  {
48  return static_cast<M &>(*this).operator()(i, j);
49  }
50 
51  double operator()(uint32_t i, uint32_t j) const
52  {
53  return static_cast<const M &>(*this).operator()(i, j);
54  }
55 };
56 
57 // --------------------------------------------------------------------
58 // matrix is m x n, addressing i,j is 0 <= i < m and 0 <= j < n
59 // element m i,j is mapped to [i * n + j] and thus storage is row major
60 
61 class Matrix : public MatrixExpression<Matrix>
62 {
63  public:
64  template <typename M2>
66  : m_m(m.dim_m())
67  , m_n(m.dim_n())
68  , m_data(m_m * m_n)
69  {
70  for (uint32_t i = 0; i < m_m; ++i)
71  {
72  for (uint32_t j = 0; j < m_n; ++j)
73  operator()(i, j) = m(i, j);
74  }
75  }
76 
77  Matrix(size_t m, size_t n, double v = 0)
78  : m_m(m)
79  , m_n(n)
80  , m_data(m_m * m_n)
81  {
82  std::fill(m_data.begin(), m_data.end(), v);
83  }
84 
85  Matrix() = default;
86  Matrix(Matrix &&m) = default;
87  Matrix(const Matrix &m) = default;
88  Matrix &operator=(Matrix &&m) = default;
89  Matrix &operator=(const Matrix &m) = default;
90 
91  size_t dim_m() const { return m_m; }
92  size_t dim_n() const { return m_n; }
93 
94  double operator()(size_t i, size_t j) const
95  {
96  assert(i < m_m);
97  assert(j < m_n);
98  return m_data[i * m_n + j];
99  }
100 
101  double &operator()(size_t i, size_t j)
102  {
103  assert(i < m_m);
104  assert(j < m_n);
105  return m_data[i * m_n + j];
106  }
107 
108  private:
109  size_t m_m = 0, m_n = 0;
110  std::vector<double> m_data;
111 };
112 
113 // --------------------------------------------------------------------
114 
115 class SymmetricMatrix : public MatrixExpression<SymmetricMatrix>
116 {
117  public:
118  SymmetricMatrix(uint32_t n, double v = 0)
119  : m_n(n)
120  , m_data((m_n * (m_n + 1)) / 2)
121  {
122  std::fill(m_data.begin(), m_data.end(), v);
123  }
124 
125  SymmetricMatrix() = default;
126  SymmetricMatrix(SymmetricMatrix &&m) = default;
127  SymmetricMatrix(const SymmetricMatrix &m) = default;
128  SymmetricMatrix &operator=(SymmetricMatrix &&m) = default;
129  SymmetricMatrix &operator=(const SymmetricMatrix &m) = default;
130 
131  uint32_t dim_m() const { return m_n; }
132  uint32_t dim_n() const { return m_n; }
133 
134  double operator()(uint32_t i, uint32_t j) const
135  {
136  return i < j
137  ? m_data[(j * (j + 1)) / 2 + i]
138  : m_data[(i * (i + 1)) / 2 + j];
139  }
140 
141  double &operator()(uint32_t i, uint32_t j)
142  {
143  if (i > j)
144  std::swap(i, j);
145  assert(j < m_n);
146  return m_data[(j * (j + 1)) / 2 + i];
147  }
148 
149  private:
150  uint32_t m_n;
151  std::vector<double> m_data;
152 };
153 
154 class IdentityMatrix : public MatrixExpression<IdentityMatrix>
155 {
156  public:
157  IdentityMatrix(uint32_t n)
158  : m_n(n)
159  {
160  }
161 
162  uint32_t dim_m() const { return m_n; }
163  uint32_t dim_n() const { return m_n; }
164 
165  double operator()(uint32_t i, uint32_t j) const
166  {
167  return i == j ? 1 : 0;
168  }
169 
170  private:
171  uint32_t m_n;
172 };
173 
174 // --------------------------------------------------------------------
175 // matrix functions, implemented as expression templates
176 
177 template <typename M1, typename M2>
178 class MatrixSubtraction : public MatrixExpression<MatrixSubtraction<M1, M2>>
179 {
180  public:
181  MatrixSubtraction(const M1 &m1, const M2 &m2)
182  : m_m1(m1)
183  , m_m2(m2)
184  {
185  assert(m_m1.dim_m() == m_m2.dim_m());
186  assert(m_m1.dim_n() == m_m2.dim_n());
187  }
188 
189  uint32_t dim_m() const { return m_m1.dim_m(); }
190  uint32_t dim_n() const { return m_m1.dim_n(); }
191 
192  double operator()(uint32_t i, uint32_t j) const
193  {
194  return m_m1(i, j) - m_m2(i, j);
195  }
196 
197  private:
198  const M1 &m_m1;
199  const M2 &m_m2;
200 };
201 
202 template <typename M1, typename M2>
204 {
205  return MatrixSubtraction(*static_cast<const M1 *>(&m1), *static_cast<const M2 *>(&m2));
206 }
207 
208 template <typename M>
209 class MatrixMultiplication : public MatrixExpression<MatrixMultiplication<M>>
210 {
211  public:
212  MatrixMultiplication(const M &m, double v)
213  : m_m(m)
214  , m_v(v)
215  {
216  }
217 
218  uint32_t dim_m() const { return m_m.dim_m(); }
219  uint32_t dim_n() const { return m_m.dim_n(); }
220 
221  double operator()(uint32_t i, uint32_t j) const
222  {
223  return m_m(i, j) * m_v;
224  }
225 
226  private:
227  const M &m_m;
228  double m_v;
229 };
230 
231 template <typename M>
233 {
234  return MatrixMultiplication(*static_cast<const M *>(&m), v);
235 }
236 
237 // --------------------------------------------------------------------
238 
239 template <class M1>
241 {
242  Matrix cf(m.dim_m(), m.dim_m());
243 
244  const size_t ixs[4][3] = {
245  { 1, 2, 3 },
246  { 0, 2, 3 },
247  { 0, 1, 3 },
248  { 0, 1, 2 }
249  };
250 
251  for (size_t x = 0; x < 4; ++x)
252  {
253  const size_t *ix = ixs[x];
254 
255  for (size_t y = 0; y < 4; ++y)
256  {
257  const size_t *iy = ixs[y];
258 
259  cf(x, y) =
260  m(ix[0], iy[0]) * m(ix[1], iy[1]) * m(ix[2], iy[2]) +
261  m(ix[0], iy[1]) * m(ix[1], iy[2]) * m(ix[2], iy[0]) +
262  m(ix[0], iy[2]) * m(ix[1], iy[0]) * m(ix[2], iy[1]) -
263  m(ix[0], iy[2]) * m(ix[1], iy[1]) * m(ix[2], iy[0]) -
264  m(ix[0], iy[1]) * m(ix[1], iy[0]) * m(ix[2], iy[2]) -
265  m(ix[0], iy[0]) * m(ix[1], iy[2]) * m(ix[2], iy[1]);
266 
267  if ((x + y) % 2 == 1)
268  cf(x, y) *= -1;
269  }
270  }
271 
272  return cf;
273 }
274 
275 // --------------------------------------------------------------------
276 
277 template<typename T>
278 quaternion_type<T> normalize(quaternion_type<T> q)
279 {
280  std::valarray<double> t(4);
281 
282  t[0] = q.get_a();
283  t[1] = q.get_b();
284  t[2] = q.get_c();
285  t[3] = q.get_d();
286 
287  t *= t;
288 
289  double length = std::sqrt(t.sum());
290 
291  if (length > 0.001)
292  q /= static_cast<quaternion::value_type>(length);
293  else
294  q = quaternion(1, 0, 0, 0);
295 
296  return q;
297 }
298 
299 // --------------------------------------------------------------------
300 
301 quaternion construct_from_angle_axis(float angle, point axis)
302 {
303  auto q = std::cos((angle * kPI / 180) / 2);
304  auto s = std::sqrt(1 - q * q);
305 
306  axis.normalize();
307 
308  return normalize(quaternion{
309  static_cast<float>(q),
310  static_cast<float>(s * axis.m_x),
311  static_cast<float>(s * axis.m_y),
312  static_cast<float>(s * axis.m_z) });
313 }
314 
315 std::tuple<double, point> quaternion_to_angle_axis(quaternion q)
316 {
317  if (q.get_a() > 1)
318  q = normalize(q);
319 
320  // angle:
321  double angle = 2 * std::acos(q.get_a());
322  angle = angle * 180 / kPI;
323 
324  // axis:
325  float s = std::sqrt(1 - q.get_a() * q.get_a());
326  if (s < 0.001)
327  s = 1;
328 
329  point axis(q.get_b() / s, q.get_c() / s, q.get_d() / s);
330 
331  return { angle, axis };
332 }
333 
334 point center_points(std::vector<point> &Points)
335 {
336  point t;
337 
338  for (point &pt : Points)
339  {
340  t.m_x += pt.m_x;
341  t.m_y += pt.m_y;
342  t.m_z += pt.m_z;
343  }
344 
345  t.m_x /= Points.size();
346  t.m_y /= Points.size();
347  t.m_z /= Points.size();
348 
349  for (point &pt : Points)
350  {
351  pt.m_x -= t.m_x;
352  pt.m_y -= t.m_y;
353  pt.m_z -= t.m_z;
354  }
355 
356  return t;
357 }
358 
360  float angle, float esd)
361 {
362  p1 -= p3;
363  p2 -= p3;
364  p4 -= p3;
365  p3 -= p3;
366 
367  quaternion q;
368  auto axis = p2;
369 
370  float dh = dihedral_angle(p1, p2, p3, p4);
371  for (int iteration = 0; iteration < 100; ++iteration)
372  {
373  float delta = std::fmod(angle - dh, 360.0f);
374 
375  if (delta < -180)
376  delta += 360;
377  if (delta > 180)
378  delta -= 360;
379 
380  if (std::abs(delta) < esd)
381  break;
382 
383  // if (iteration > 0)
384  // std::cout << cif::coloured(("iteration " + std::to_string(iteration)).c_str(), cif::scBLUE, cif::scBLACK) << " delta: " << delta << std::endl;
385 
386  auto q2 = construct_from_angle_axis(delta, axis);
387  q = iteration == 0 ? q2 : q * q2;
388 
389  p4.rotate(q2);
390 
391  dh = dihedral_angle(p1, p2, p3, p4);
392  }
393 
394  return q;
395 }
396 
397 point centroid(const std::vector<point> &pts)
398 {
399  point result;
400 
401  for (auto &pt : pts)
402  result += pt;
403 
404  result /= static_cast<float>(pts.size());
405 
406  return result;
407 }
408 
409 double RMSd(const std::vector<point> &a, const std::vector<point> &b)
410 {
411  double sum = 0;
412  for (uint32_t i = 0; i < a.size(); ++i)
413  {
414  std::valarray<double> d(3);
415 
416  d[0] = b[i].m_x - a[i].m_x;
417  d[1] = b[i].m_y - a[i].m_y;
418  d[2] = b[i].m_z - a[i].m_z;
419 
420  d *= d;
421 
422  sum += d.sum();
423  }
424 
425  return std::sqrt(sum / a.size());
426 }
427 
428 // The next function returns the largest solution for a quartic equation
429 // based on Ferrari's algorithm.
430 // A depressed quartic is of the form:
431 //
432 // x^4 + ax^2 + bx + c = 0
433 //
434 // (since I'm too lazy to find out a better way, I've implemented the
435 // routine using complex values to avoid nan's as a result of taking
436 // sqrt of a negative number)
437 double LargestDepressedQuarticSolution(double a, double b, double c)
438 {
439  std::complex<double> P = -(a * a) / 12 - c;
440  std::complex<double> Q = -(a * a * a) / 108 + (a * c) / 3 - (b * b) / 8;
441  std::complex<double> R = -Q / 2.0 + std::sqrt((Q * Q) / 4.0 + (P * P * P) / 27.0);
442 
443  std::complex<double> U = std::pow(R, 1 / 3.0);
444 
445  std::complex<double> y;
446  if (U == 0.0)
447  y = -5.0 * a / 6.0 + U - std::pow(Q, 1.0 / 3.0);
448  else
449  y = -5.0 * a / 6.0 + U - P / (3.0 * U);
450 
451  std::complex<double> W = std::sqrt(a + 2.0 * y);
452 
453  // And to get the final result:
454  // result = (±W + std::sqrt(-(3 * alpha + 2 * y ± 2 * beta / W))) / 2;
455  // We want the largest result, so:
456 
457  std::valarray<double> t(4);
458 
459  t[0] = ((W + std::sqrt(-(3.0 * a + 2.0 * y + 2.0 * b / W))) / 2.0).real();
460  t[1] = ((W + std::sqrt(-(3.0 * a + 2.0 * y - 2.0 * b / W))) / 2.0).real();
461  t[2] = ((-W + std::sqrt(-(3.0 * a + 2.0 * y + 2.0 * b / W))) / 2.0).real();
462  t[3] = ((-W + std::sqrt(-(3.0 * a + 2.0 * y - 2.0 * b / W))) / 2.0).real();
463 
464  return t.max();
465 }
466 
467 quaternion align_points(const std::vector<point> &pa, const std::vector<point> &pb)
468 {
469  // First calculate M, a 3x3 Matrix containing the sums of products of the coordinates of A and B
470  Matrix M(3, 3, 0);
471 
472  for (uint32_t i = 0; i < pa.size(); ++i)
473  {
474  const point &a = pa[i];
475  const point &b = pb[i];
476 
477  M(0, 0) += a.m_x * b.m_x;
478  M(0, 1) += a.m_x * b.m_y;
479  M(0, 2) += a.m_x * b.m_z;
480  M(1, 0) += a.m_y * b.m_x;
481  M(1, 1) += a.m_y * b.m_y;
482  M(1, 2) += a.m_y * b.m_z;
483  M(2, 0) += a.m_z * b.m_x;
484  M(2, 1) += a.m_z * b.m_y;
485  M(2, 2) += a.m_z * b.m_z;
486  }
487 
488  // Now calculate N, a symmetric 4x4 Matrix
489  SymmetricMatrix N(4);
490 
491  N(0, 0) = M(0, 0) + M(1, 1) + M(2, 2);
492  N(0, 1) = M(1, 2) - M(2, 1);
493  N(0, 2) = M(2, 0) - M(0, 2);
494  N(0, 3) = M(0, 1) - M(1, 0);
495 
496  N(1, 1) = M(0, 0) - M(1, 1) - M(2, 2);
497  N(1, 2) = M(0, 1) + M(1, 0);
498  N(1, 3) = M(0, 2) + M(2, 0);
499 
500  N(2, 2) = -M(0, 0) + M(1, 1) - M(2, 2);
501  N(2, 3) = M(1, 2) + M(2, 1);
502 
503  N(3, 3) = -M(0, 0) - M(1, 1) + M(2, 2);
504 
505  // det(N - λI) = 0
506  // find the largest λ (λm)
507  //
508  // Aλ4 + Bλ3 + Cλ2 + Dλ + E = 0
509  // A = 1
510  // B = 0
511  // and so this is a so-called depressed quartic
512  // solve it using Ferrari's algorithm
513 
514  double C = -2 * (M(0, 0) * M(0, 0) + M(0, 1) * M(0, 1) + M(0, 2) * M(0, 2) +
515  M(1, 0) * M(1, 0) + M(1, 1) * M(1, 1) + M(1, 2) * M(1, 2) +
516  M(2, 0) * M(2, 0) + M(2, 1) * M(2, 1) + M(2, 2) * M(2, 2));
517 
518  double D = 8 * (M(0, 0) * M(1, 2) * M(2, 1) +
519  M(1, 1) * M(2, 0) * M(0, 2) +
520  M(2, 2) * M(0, 1) * M(1, 0)) -
521  8 * (M(0, 0) * M(1, 1) * M(2, 2) +
522  M(1, 2) * M(2, 0) * M(0, 1) +
523  M(2, 1) * M(1, 0) * M(0, 2));
524 
525  // E is the determinant of N:
526  double E =
527  (N(0, 0) * N(1, 1) - N(0, 1) * N(0, 1)) * (N(2, 2) * N(3, 3) - N(2, 3) * N(2, 3)) +
528  (N(0, 1) * N(0, 2) - N(0, 0) * N(2, 1)) * (N(2, 1) * N(3, 3) - N(2, 3) * N(1, 3)) +
529  (N(0, 0) * N(1, 3) - N(0, 1) * N(0, 3)) * (N(2, 1) * N(2, 3) - N(2, 2) * N(1, 3)) +
530  (N(0, 1) * N(2, 1) - N(1, 1) * N(0, 2)) * (N(0, 2) * N(3, 3) - N(2, 3) * N(0, 3)) +
531  (N(1, 1) * N(0, 3) - N(0, 1) * N(1, 3)) * (N(0, 2) * N(2, 3) - N(2, 2) * N(0, 3)) +
532  (N(0, 2) * N(1, 3) - N(2, 1) * N(0, 3)) * (N(0, 2) * N(1, 3) - N(2, 1) * N(0, 3));
533 
534  // solve quartic
535  double lambda = LargestDepressedQuarticSolution(C, D, E);
536 
537  // calculate t = (N - λI)
538  Matrix t = N - IdentityMatrix(4) * lambda;
539 
540  // calculate a Matrix of cofactors for t
541  Matrix cf = Cofactors(t);
542 
543  int maxR = 0;
544  for (int r = 1; r < 4; ++r)
545  {
546  if (std::abs(cf(r, 0)) > std::abs(cf(maxR, 0)))
547  maxR = r;
548  }
549 
550  quaternion q(
551  static_cast<float>(cf(maxR, 0)),
552  static_cast<float>(cf(maxR, 1)),
553  static_cast<float>(cf(maxR, 2)),
554  static_cast<float>(cf(maxR, 3)));
555  q = normalize(q);
556 
557  return q;
558 }
559 
560 // --------------------------------------------------------------------
561 
562 point nudge(point p, float offset)
563 {
564  static const float kPI_f = static_cast<float>(kPI);
565 
566  static std::random_device rd;
567  static std::mt19937_64 rng(rd());
568 
569  std::uniform_real_distribution<float> randomAngle(0, 2 * kPI_f);
570  std::normal_distribution<float> randomOffset(0, offset);
571 
572  float theta = randomAngle(rng);
573  float phi1 = randomAngle(rng) - kPI_f;
574  float phi2 = randomAngle(rng) - kPI_f;
575 
576  quaternion q = spherical(1.0f, theta, phi1, phi2);
577 
578  point r{ 0, 0, 1 };
579  r.rotate(q);
580  r *= randomOffset(rng);
581 
582  return p + r;
583 }
584 
585 } // namespace cif
Matrix Cofactors(const M1 &m)
Definition: point.cpp:240
double operator()(uint32_t i, uint32_t j) const
Definition: point.cpp:221
quaternion align_points(const std::vector< point > &pa, const std::vector< point > &pb)
Definition: point.cpp:467
n The following was calculated during iteration
point center_points(std::vector< point > &Points)
Definition: point.cpp:334
point centroid(const std::vector< point > &pts)
Definition: point.cpp:397
double & operator()(uint32_t i, uint32_t j)
Definition: point.cpp:46
doublereal * c
uint32_t dim_n() const
Definition: point.cpp:190
MatrixSubtraction< M1, M2 > operator-(const MatrixExpression< M1 > &m1, const MatrixExpression< M2 > &m2)
Definition: point.cpp:203
void sqrt(Image< double > &op)
size_t dim_m() const
Definition: point.cpp:91
static double * y
void abs(Image< double > &op)
MatrixSubtraction(const M1 &m1, const M2 &m2)
Definition: point.cpp:181
double operator()(size_t i, size_t j) const
Definition: point.cpp:94
quaternion construct_for_dihedral_angle(point p1, point p2, point p3, point p4, float angle, float esd)
Definition: point.cpp:359
double & operator()(size_t i, size_t j)
Definition: point.cpp:101
doublereal * x
#define i
uint32_t dim_m() const
Definition: point.cpp:189
doublereal * d
double theta
uint32_t dim_m() const
Definition: point.cpp:162
#define M2
doublereal * b
#define M1
char axis
double * lambda
double * f
uint32_t dim_m() const
Definition: point.cpp:131
double RMSd(const std::vector< point > &a, const std::vector< point > &b)
Definition: point.cpp:409
quaternion_type< T > normalize(quaternion_type< T > q)
Definition: point.cpp:278
__host__ __device__ float length(float2 v)
quaternion construct_from_angle_axis(float angle, point axis)
Definition: point.cpp:301
MatrixMultiplication(const M &m, double v)
Definition: point.cpp:212
#define q2
#define j
int m
double operator()(uint32_t i, uint32_t j) const
Definition: point.cpp:165
double LargestDepressedQuarticSolution(double a, double b, double c)
Definition: point.cpp:437
uint32_t dim_n() const
Definition: point.cpp:219
point nudge(point p, float offset)
Definition: point.cpp:562
uint32_t dim_m() const
Definition: point.cpp:218
double operator()(uint32_t i, uint32_t j) const
Definition: point.cpp:134
IdentityMatrix(uint32_t n)
Definition: point.cpp:157
MatrixMultiplication< M > operator*(const MatrixExpression< M > &m, double v)
Definition: point.cpp:232
std::tuple< double, point > quaternion_to_angle_axis(quaternion q)
Definition: point.cpp:315
Matrix(const MatrixExpression< M2 > &m)
Definition: point.cpp:65
uint32_t dim_m() const
Definition: point.cpp:43
double & operator()(uint32_t i, uint32_t j)
Definition: point.cpp:141
Matrix(size_t m, size_t n, double v=0)
Definition: point.cpp:77
uint32_t dim_n() const
Definition: point.cpp:44
double operator()(uint32_t i, uint32_t j) const
Definition: point.cpp:192
int * n
doublereal * a
double operator()(uint32_t i, uint32_t j) const
Definition: point.cpp:51
size_t dim_n() const
Definition: point.cpp:92
uint32_t dim_n() const
Definition: point.cpp:163
uint32_t dim_n() const
Definition: point.cpp:132
double * delta
SymmetricMatrix(uint32_t n, double v=0)
Definition: point.cpp:118