Developer documentation
Version 3.0.3-105-gd3941f44
median.h
Go to the documentation of this file.
1/* Copyright (c) 2008-2022 the MRtrix3 contributors.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * Covered Software is provided under this License on an "as is"
8 * basis, without warranty of any kind, either expressed, implied, or
9 * statutory, including, without limitation, warranties that the
10 * Covered Software is free of defects, merchantable, fit for a
11 * particular purpose or non-infringing.
12 * See the Mozilla Public License v. 2.0 for more details.
13 *
14 * For more details, see http://www.mrtrix.org/.
15 */
16
17#ifndef __math_median_h__
18#define __math_median_h__
19
20#include <algorithm>
21#include <limits>
22
23#include "types.h"
24#include "app.h"
25
26
27namespace MR
28{
29 namespace Math
30 {
31
32 namespace {
33 template <typename X>
34 inline bool not_a_number (X x) {
35 return false;
36 }
37
38 template <> inline bool not_a_number (float x) { return std::isnan (x); }
39 template <> inline bool not_a_number (double x) { return std::isnan (x); }
40 }
41
42
43
44 template <class Container>
45 inline typename Container::value_type median (Container& list)
46 {
47 size_t num = list.size();
48 // remove NaNs:
49 for (size_t n = 0; n < num; ++n) {
50 while (not_a_number (list[n]) && n < num) {
51 --num;
52 //std::swap (list[n], list[num]);
53 // Commented std::swap to provide bool compatibility
54 typename Container::value_type temp = list[num]; list[num] = list[n]; list[n] = temp;
55 }
56 }
57 if (!num)
58 return std::numeric_limits<typename Container::value_type>::quiet_NaN();
59
60 size_t middle = num/2;
61 std::nth_element (list.begin(), list.begin()+middle, list.begin()+num);
62 typename Container::value_type med_val = list[middle];
63 if (!(num&1U)) {
64 --middle;
65 std::nth_element (list.begin(), list.begin()+middle, list.begin()+middle+1);
66 med_val = (med_val + list[middle])/2.0;
67 }
68 return med_val;
69 }
70
71
72 // Weiszfeld median
73 template <class MatrixType = Eigen::Matrix<default_type, 3, Eigen::Dynamic>, class VectorType = Eigen::Matrix<default_type, 3, 1>>
74 bool median_weiszfeld(const MatrixType& X, VectorType& median, const size_t numIter = 300, const default_type precision = 0.00001) {
75 assert(X.cols() >= 2 && "cannot compute weiszfeld median for less than two points");
76 assert(X.rows() >= 2 && "weisfeld median for one dimensional data is not unique. did you mean the median?");
77 const size_t dimensionality = X.rows();
78
79 // initialise to the centroid
80 assert(median.rows() >= 2);
81 assert(median.rows() == X.rows());
82 median = X.transpose().colwise().mean();
83 size_t m = X.cols();
84 // If the init point is in the set of points, we shift it:
85 size_t n = (X.colwise() - median).colwise().squaredNorm().nonZeros();
86 while (n != m){ // (X.colwise() - median).colwise().squaredNorm().transpose().colwise().minCoeff() > 0.000001;
87 median(0) += 10 * precision;
88 n = (X.colwise() - median).colwise().squaredNorm().nonZeros();
89 }
90
91 bool convergence = false;
92 vector<default_type> dist(numIter);
93
94 // Minimizing the sum of the squares of the distances between each point in 'X' and the median.
95 size_t i = 0;
96 Eigen::Matrix<default_type, Eigen::Dynamic, 1> s1;
97 s1.resize(dimensionality,1);
98 while ( !convergence && (i < numIter) ) {
99 default_type norm = 0.0;
100 s1.setZero();
101 default_type denum = 0.0;
102 default_type sdist = 0.0;
103 for (size_t j = 0; j < m; ++j){
104 norm = (X.col(j) - median).norm();
105 s1 += X.col(j) / norm;
106 denum += 1.0 / norm;
107 sdist += norm;
108 }
109 dist[i] = sdist;
110 if (denum == 0.0 or std::isnan(denum)){
111 WARN ("Could not compute geometric median!" );
112 break;
113 }
114
115 median = s1 / denum;
116 if (i > 3){
117 convergence=(abs(dist[i]-dist[i-2])<precision);
118 }
119 ++i;
120 }
121 if (i == numIter)
122 WARN ("Weiszfeld's median algorithm did not converge after "+str(numIter)+" iterations");
123 // std::cerr << str(dist) << std::endl;
124 return convergence;
125 }
126 }
127}
128
129
130#endif
131
#define WARN(msg)
Definition: exception.h:73
MR::default_type value_type
Definition: typedefs.h:33
bool median_weiszfeld(const MatrixType &X, VectorType &median, const size_t numIter=300, const default_type precision=0.00001)
Definition: median.h:74
Container::value_type median(Container &list)
Definition: median.h:45
Definition: base.h:24
double default_type
the default type used throughout MRtrix
Definition: types.h:228
constexpr std::enable_if< std::is_arithmetic< X >::value &&std::is_unsigned< X >::value, X >::type abs(X x)
Definition: types.h:297
std::string str(const T &value, int precision=0)
Definition: mrtrix.h:247
size_t num
Definition: thread.h:216