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