chernoff.cpp
Go to the documentation of this file.
1 #include "chernoff.h"
2 
3 #include <cmath>
4 #include <Eigen/Core>
5 #include <Eigen/Eigenvalues>
6 
8 {
9 
11  //TODO: there seems to be an error that leads to the mean vectors always having the same
12  //value, which leads to the diff being zero which leads to distance being 0/inf/nan
13  //aron::data::NDArray image1 = *p1;
14  //aron::data::NDArray image2 = *p2;
15 
16  //then normalize them to have values between 0 and 1
17  //TODO: do not rturn new array but change existing one
18  auto f = normalize_ndarray(p1, 255);
19  auto s = normalize_ndarray(p2, 255);
20  //ARMARX_INFO << VAROUT(f);
21  //ARMARX_INFO << VAROUT(s);
22  //then calculate the mean vectors
23  std::vector<double> mean_one = calculate_mean_values(f);
24  std::vector<double> mean_two = calculate_mean_values(s);
25  //ARMARX_INFO << VAROUT(mean_one);
26  //then calculate the covariance matrices
27  std::vector<std::vector<double>> cov_one = calculate_covariance_matrix(f, mean_one);
28  std::vector<std::vector<double>> cov_two = calculate_covariance_matrix(s, mean_two);
29  //then calculate the Bhattacharyya distance and return
30  double distance = calculate_bhattacharyya_distance(mean_one, mean_two, cov_one, cov_two);
31  ARMARX_INFO << "Chernoff distance: " << std::to_string(distance);
32  return distance;
33  }
34 
35  std::vector<double> calculate_mean_values(data::NDArray array)
36  {
37  std::vector<double> mean(3, 0.0);
38  int width = array.getShape().at(0);
39  int height = array.getShape().at(1);
40  int colors = array.getShape().at(2);
41  auto data = array.getDataAsVector();
42 
43  for (int row = 0; row < height; row++){
44  for (int col = 0; col < width; col++){
45  double red = data.at(row * width * colors + col * colors);
46  double green = data.at(row * width * colors + col * colors + 1);
47  double blue = data.at(row * width * colors + col * colors + 2);
48  mean[0] += red;
49  mean[1] += green;
50  mean[2] += blue;
51  }
52  }
53  //ARMARX_INFO << VAROUT(mean);
54  int numPixels = width * height; //only per color
55  mean[0] /= numPixels;
56  mean[1] /= numPixels;
57  mean[2] /= numPixels;
58 
59  return mean;
60  }
61 
63  {
64  std::vector<unsigned char> data = array->getDataAsVector();
65  std::vector<unsigned char> new_data(data.size());
66 
67  for(int i = 0; i < data.size(); i++){
68  double a = data.at(i);
69  double n = a/j;
70  new_data.at(i) = n;
71  }
72 
73  armarx::aron::data::NDArray n(array->getShape(), array->getType(),new_data, array->getPath());
74  return n;
75  }
76 
77  std::vector<std::vector<double> > calculate_covariance_matrix(data::NDArray array, std::vector<double> mean = std::vector<double>())
78  {
79  int width = array.getShape().at(0);
80  int height = array.getShape().at(1);
81  int colors = array.getShape().at(2);
82  auto data = array.getDataAsVector();
83 
84  std::vector<double> mean_vec(3, 0.0);
85  if(mean.empty()){
86  mean_vec = calculate_mean_values(array);
87  } else {
88  mean_vec = mean;
89  }
90 
91  int numPixels = width * height; //only per color
92 
93  std::vector<std::vector<double>> covariance(3, std::vector<double>(3, 0.0));
94 
95  for(int row = 0; row < height; row++){
96  for(int col = 0; col < width; col++){
97  double red = data.at(row * width * colors + col * colors);
98  double green = data.at(row * width * colors + col * colors + 1);
99  double blue = data.at(row * width * colors + col * colors + 2);
100 
101  double redDeviation = red - mean[0];
102  double greenDeviation = green - mean[1];
103  double blueDeviation = blue - mean[2];
104 
105  covariance[0][0] += redDeviation * redDeviation;
106  covariance[0][1] += redDeviation * greenDeviation;
107  covariance[0][2] += redDeviation * blueDeviation;
108  covariance[1][1] += greenDeviation * greenDeviation;
109  covariance[1][2] += greenDeviation * blueDeviation;
110  covariance[2][2] += blueDeviation * blueDeviation;
111  }
112  }
113 
114  for(int i = 0; i < 3; i++){
115  for (int j = 0; j < i; j++){
116  covariance[i][j] /= numPixels;
117  covariance[j][i] = covariance[i][j];
118  }
119  covariance[i][i] /= numPixels;
120  }
121 
122  return covariance;
123  }
124 
125  double calculate_bhattacharyya_distance(std::vector<double> mean_one, std::vector<double> mean_two, std::vector<std::vector<double> > covariance_one, std::vector<std::vector<double> > covariance_two)
126  {
127  //first make mean vectors and covariance matrices in Eigen:: objects
128  Eigen::VectorXd meanOne(3);
129  meanOne << mean_one[0], mean_one[1], mean_one[2];
130  Eigen::VectorXd meanTwo(3);
131  meanTwo << mean_two[0], mean_two[1], mean_two[2];
132 
133  //ARMARX_INFO << VAROUT(meanOne);
134  //ARMARX_INFO << VAROUT(meanTwo);
135 
136  //these are column-major instead or row-major, but we have a quadratic, symmetric matrix, so it does not matter
137  //this conversion does not work correctly!!
138  //Eigen::MatrixXd cov_one = Eigen::Map<Eigen::MatrixXd>(covariance_one[0].data(), covariance_one.size(), covariance_one[0].size());
139  //Eigen::MatrixXd cov_two = Eigen::Map<Eigen::MatrixXd>(covariance_two[0].data(), covariance_two.size(), covariance_two[0].size());
140 
141  Eigen::Matrix3d cov_one;
142  cov_one << covariance_one[0][0], covariance_one[0][1], covariance_one[0][2],
143  covariance_one[1][0], covariance_one[1][1], covariance_one[1][2],
144  covariance_one[2][0], covariance_one[2][1], covariance_one[2][2];
145 
146  Eigen::Matrix3d cov_two;
147  cov_two << covariance_two[0][0], covariance_two[0][1], covariance_two[0][2],
148  covariance_two[1][0], covariance_two[1][1], covariance_two[1][2],
149  covariance_two[2][0], covariance_two[2][1], covariance_two[2][2];
150 
151 
152  //ARMARX_INFO << VAROUT(cov_one);
153 
154  Eigen::VectorXd meanDiff = meanOne - meanTwo;
155  //ARMARX_INFO << VAROUT(meanDiff);
156 
157  Eigen::MatrixXd sigma = 0.5 * (cov_one + cov_two);
158  Eigen::MatrixXd sigma_inverse = sigma.inverse();
159 
160  double det_cov_one = cov_one.determinant();
161  double det_cov_two = cov_two.determinant();
162  double det_sigma = sigma.determinant();
163 
164  //formula from https://www.kaggle.com/code/debanga/statistical-distances
165  double bigger = meanDiff.transpose() * (sigma_inverse) * meanDiff;
166  //ARMARX_INFO << "Bigger: " << std::to_string(bigger);
167  double term_one = 0.125 * bigger;
168  double sqr = std::sqrt(det_cov_one * det_cov_two);
169  //ARMARX_INFO << "Sqr: " << std::to_string(sqr);
170  double before_log = det_sigma / (sqr);
171  //ARMARX_INFO << "Before log: " << std::to_string(before_log);
172  double term_two = 0.5 * std::log(before_log);
173 
174  double distance = term_one + term_two;
175 
176  return distance;
177  }
178 
179 
180 }
GfxTL::sqrt
VectorXD< D, T > sqrt(const VectorXD< D, T > &a)
Definition: VectorXD.h:662
armarx::aron::data::NDArray::getDataAsVector
std::vector< unsigned char > getDataAsVector() const
Definition: NDArray.cpp:139
armarx::aron::similarity::chernoff
Definition: chernoff.cpp:7
armarx::aron::data::NDArray
Definition: NDArray.h:48
armarx::aron::similarity::chernoff::calculate_mean_values
std::vector< double > calculate_mean_values(data::NDArray array)
Definition: chernoff.cpp:35
armarx::aron::similarity::chernoff::normalize_ndarray
data::NDArray normalize_ndarray(data::NDArrayPtr array, int j)
Definition: chernoff.cpp:62
armarx::aron::data::NDArrayPtr
std::shared_ptr< NDArray > NDArrayPtr
Definition: NDArray.h:46
armarx::aron::data::NDArray::getShape
std::vector< int > getShape() const
Definition: NDArray.cpp:145
armarx::ctrlutil::a
double a(double t, double a0, double j)
Definition: CtrlUtil.h:45
armarx::mean
std::optional< float > mean(const boost::circular_buffer< NameValueMap > &buffer, const std::string &key)
Definition: KinematicUnitGuiPlugin.cpp:1615
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::aron::similarity::chernoff::compute_similarity
double compute_similarity(const aron::data::NDArrayPtr p1, const aron::data::NDArrayPtr p2)
Definition: chernoff.cpp:10
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
armarx::red
QColor red()
Definition: StyleSheets.h:76
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::aron::similarity::chernoff::calculate_covariance_matrix
std::vector< std::vector< double > > calculate_covariance_matrix(data::NDArray array, std::vector< double > mean=std::vector< double >())
Definition: chernoff.cpp:77
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:88
armarx::aron::similarity::chernoff::calculate_bhattacharyya_distance
double calculate_bhattacharyya_distance(std::vector< double > mean_one, std::vector< double > mean_two, std::vector< std::vector< double > > covariance_one, std::vector< std::vector< double > > covariance_two)
Definition: chernoff.cpp:125
chernoff.h
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx::green
QColor green()
Definition: StyleSheets.h:72