1 /*
2 * Medical Image Registration ToolKit (MIRTK)
3 *
4 * Copyright 2008-2017 Imperial College London
5 * Copyright 2008-2015 Daniel Rueckert, Julia Schnabel
6 * Copyright 2015-2017 Andreas Schuh
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21 #include "mirtk/Histogram1D.h"
22 #include "mirtk/Histogram2D.h"
23
24 #include "mirtk/Math.h"
25 #include "mirtk/Memory.h"
26 #include "mirtk/Parallel.h"
27 #include "mirtk/GenericImage.h"
28
29 #include "mirtk/CommonExport.h"
30
31
32 namespace mirtk {
33
34
35 // Global "debug" flag (cf. mirtk/Options.h)
36 MIRTK_Common_EXPORT extern int debug;
37
38
39 // =============================================================================
40 // Auxiliary functors
41 // =============================================================================
42
43 namespace Histogram2DUtils {
44
45
46 // -----------------------------------------------------------------------------
47 template <class T>
48 class LogTransform
49 {
50 T *_Bins;
51 double _Num;
52
53 public:
54
operator ()(const blocked_range<int> & re) const55 void operator ()(const blocked_range<int> &re) const
56 {
57 double p;
58 T *ptr = _Bins + re.begin();
59 for (int i = re.begin(); i != re.end(); ++i, ++ptr) {
60 p = static_cast<double>(*ptr);
61 if (p > .0) p = log(p / _Num);
62 else p = .0;
63 (*ptr) = static_cast<T>(p);
64 }
65 }
66
Run(Histogram2D<T> * hxy)67 static void Run(Histogram2D<T> *hxy)
68 {
69 LogTransform body;
70 body._Bins = hxy->RawPointer();
71 body._Num = hxy->NumberOfSamples();
72 blocked_range<int> i(0, hxy->NumberOfBins());
73 parallel_for(i, body);
74 }
75 };
76
77
78 } // namespace Histogram2DUtils
79 using namespace Histogram2DUtils;
80
81 // =============================================================================
82 // Histogram2D
83 // =============================================================================
84
85 // -----------------------------------------------------------------------------
86 template <class HistogramType>
Histogram2D(const Histogram2D & h)87 Histogram2D<HistogramType>::Histogram2D(const Histogram2D &h)
88 :
89 Object(h)
90 {
91 _min_x = h._min_x;
92 _min_y = h._min_y;
93 _max_x = h._max_x;
94 _max_y = h._max_y;
95 _width_x = h._width_x;
96 _width_y = h._width_y;
97 _nbins_x = h._nbins_x;
98 _nbins_y = h._nbins_y;
99 _nsamp = h._nsamp;
100 const int nbins = NumberOfBins();
101 if (nbins > 0) {
102 Allocate(_bins, _nbins_x, _nbins_y);
103 memcpy(RawPointer(), h.RawPointer(), nbins * sizeof(HistogramType));
104 } else {
105 _bins = nullptr;
106 }
107 }
108
109 // -----------------------------------------------------------------------------
110 template <class HistogramType>
Histogram2D(int nbins_x,int nbins_y)111 Histogram2D<HistogramType>::Histogram2D(int nbins_x, int nbins_y)
112 {
113 if (nbins_x < 0) nbins_x = 0;
114 if (nbins_y < 0) nbins_y = 0;
115 _min_x = 0;
116 _min_y = 0;
117 _max_x = nbins_x;
118 _max_y = nbins_y;
119 _width_x = 1;
120 _width_y = 1;
121 _nbins_x = nbins_x;
122 _nbins_y = nbins_y;
123 _nsamp = 0;
124 if (nbins_x > 0 && nbins_y > 0) {
125 CAllocate(_bins, _nbins_x, _nbins_y);
126 } else {
127 _bins = nullptr;
128 }
129 }
130
131 // -----------------------------------------------------------------------------
132 template <class HistogramType>
Histogram2D(double min_x,double max_x,double width_x,double min_y,double max_y,double width_y)133 Histogram2D<HistogramType>::Histogram2D(double min_x, double max_x, double width_x,
134 double min_y, double max_y, double width_y)
135 {
136 _min_x = min_x;
137 _min_y = min_y;
138 _max_x = max_x;
139 _max_y = max_y;
140 _nbins_x = iround((max_x - min_x) / width_x);
141 _nbins_y = iround((max_y - min_y) / width_y);
142 _width_x = (_max_x - _min_x) / double(_nbins_x);
143 _width_y = (_max_y - _min_y) / double(_nbins_y);
144 _nsamp = 0;
145 if ((_nbins_x < 1) || (_nbins_y < 1)) {
146 cerr << "Histogram2D<HistogramType>::Histogram2D: Should have at least one bin" << endl;
147 exit(1);
148 }
149 CAllocate(_bins, _nbins_x, _nbins_y);
150 }
151
152 // -----------------------------------------------------------------------------
153 template <class HistogramType>
~Histogram2D()154 Histogram2D<HistogramType>::~Histogram2D()
155 {
156 Deallocate(_bins);
157 _nbins_x = 0;
158 _nbins_y = 0;
159 _min_x = 0;
160 _min_y = 0;
161 _max_x = 0;
162 _max_y = 0;
163 _width_x = 0;
164 _width_y = 0;
165 _nsamp = 0;
166 }
167
168 // -----------------------------------------------------------------------------
169 template <class HistogramType>
Initialize(double min_x,double max_x,double width_x,double min_y,double max_y,double width_y)170 void Histogram2D<HistogramType>::Initialize(double min_x, double max_x, double width_x,
171 double min_y, double max_y, double width_y)
172 {
173 const int nbins_x = iround((max_x - min_x) / width_x);
174 const int nbins_y = iround((max_y - min_y) / width_y);
175 if (nbins_x < 1 || nbins_y < 1) {
176 cerr << "Histogram2D<HistogramType>::Histogram2D: Should have at least one bin" << endl;
177 exit(1);
178 }
179 if (_nbins_x != nbins_x || nbins_y != _nbins_y) {
180 Deallocate(_bins);
181 Allocate(_bins, nbins_x, nbins_y);
182 }
183 _min_x = min_x;
184 _min_y = min_y;
185 _max_x = max_x;
186 _max_y = max_y;
187 _nbins_x = nbins_x;
188 _nbins_y = nbins_y;
189 _width_x = (_max_x - _min_x) / double(_nbins_x);
190 _width_y = (_max_y - _min_y) / double(_nbins_y);
191 Reset();
192 }
193
194 // -----------------------------------------------------------------------------
195 template <class HistogramType>
Reset()196 void Histogram2D<HistogramType>::Reset()
197 {
198 memset(RawPointer(), 0, NumberOfBins() * sizeof(HistogramType));
199 _nsamp = 0;
200 }
201
202 // -----------------------------------------------------------------------------
203 template <class HistogramType>
Reset(const Histogram2D & h)204 void Histogram2D<HistogramType>::Reset(const Histogram2D &h)
205 {
206 if (this != &h) {
207 if ((_nbins_x != h._nbins_x) || (_nbins_y != h._nbins_y)) {
208 Deallocate(_bins);
209 Allocate(_bins, h._nbins_x, h._nbins_y);
210 }
211 _min_x = h._min_x;
212 _min_y = h._min_y;
213 _max_x = h._max_x;
214 _max_y = h._max_y;
215 _width_x = h._width_x;
216 _width_y = h._width_y;
217 _nbins_x = h._nbins_x;
218 _nbins_y = h._nbins_y;
219 _nsamp = h._nsamp;
220 memcpy(RawPointer(), h.RawPointer(), NumberOfBins() * sizeof(HistogramType));
221 }
222 }
223
224 // -----------------------------------------------------------------------------
225 template <class HistogramType>
Transpose()226 Histogram2D<HistogramType> &Histogram2D<HistogramType>::Transpose()
227 {
228 swap(_min_x, _min_y);
229 swap(_max_x, _max_y);
230 swap(_width_x, _width_y);
231 swap(_nbins_x, _nbins_y);
232
233 HistogramType **bins = Allocate<HistogramType>(_nbins_x, _nbins_y);
234 for (int j = 0; j < _nbins_y; ++j)
235 for (int i = 0; i < _nbins_x; ++i) {
236 bins[j][i] = _bins[i][j];
237 }
238 Deallocate(_bins);
239 _bins = bins;
240
241 return *this;
242 }
243
244 // -----------------------------------------------------------------------------
245 template <class HistogramType>
Transposed() const246 Histogram2D<HistogramType> Histogram2D<HistogramType>::Transposed() const
247 {
248 Histogram2D<HistogramType> h(_nbins_y, _nbins_x);
249
250 h._min_x = _min_y;
251 h._max_x = _max_y;
252 h._width_x = _width_y;
253 h._nbins_x = _nbins_y;
254
255 h._min_y = _min_x;
256 h._max_y = _max_x;
257 h._width_y = _width_x;
258 h._nbins_y = _nbins_x;
259
260 for (int j = 0; j < _nbins_y; ++j)
261 for (int i = 0; i < _nbins_x; ++i) {
262 h._bins[i][j] = _bins[j][i];
263 }
264 h._nsamp = _nsamp;
265
266 return h;
267 }
268
269 // -----------------------------------------------------------------------------
270 template <class HistogramType>
PutMin(double min_x,double min_y)271 void Histogram2D<HistogramType>::PutMin(double min_x, double min_y)
272 {
273 _min_x = min_x;
274 _min_y = min_y;
275 _width_x = (_max_x - _min_x) / (double)_nbins_x;
276 _width_y = (_max_y - _min_y) / (double)_nbins_y;
277 this->Reset();
278 }
279
280 // -----------------------------------------------------------------------------
281 template <class HistogramType>
GetMin(double * min_x,double * min_y) const282 void Histogram2D<HistogramType>::GetMin(double *min_x, double *min_y) const
283 {
284 *min_x = _min_x;
285 *min_y = _min_y;
286 }
287
288 // -----------------------------------------------------------------------------
289 template <class HistogramType>
GetMin(double & min_x,double & min_y) const290 void Histogram2D<HistogramType>::GetMin(double &min_x, double &min_y) const
291 {
292 min_x = _min_x;
293 min_y = _min_y;
294 }
295
296 // -----------------------------------------------------------------------------
297 template <class HistogramType>
PutMax(double max_x,double max_y)298 void Histogram2D<HistogramType>::PutMax(double max_x, double max_y)
299 {
300 _max_x = max_x;
301 _max_y = max_y;
302 _width_x = (_max_x - _min_x) / (double)_nbins_x;
303 _width_y = (_max_y - _min_y) / (double)_nbins_y;
304 this->Reset();
305 }
306
307 // -----------------------------------------------------------------------------
308 template <class HistogramType>
GetMax(double * max_x,double * max_y) const309 void Histogram2D<HistogramType>::GetMax(double *max_x, double *max_y) const
310 {
311 *max_x = _max_x;
312 *max_y = _max_y;
313 }
314
315 // -----------------------------------------------------------------------------
316 template <class HistogramType>
GetMax(double & max_x,double & max_y) const317 void Histogram2D<HistogramType>::GetMax(double &max_x, double &max_y) const
318 {
319 max_x = _max_x;
320 max_y = _max_y;
321 }
322
323 // -----------------------------------------------------------------------------
324 template <class HistogramType>
PutWidth(double width_x,double width_y)325 void Histogram2D<HistogramType>::PutWidth(double width_x, double width_y)
326 {
327 if ((_nbins_x > 0) && (_nbins_y > 0)) {
328 Deallocate(_bins);
329 }
330 const int nbins_x = iround((_max_x - _min_x) / width_x);
331 const int nbins_y = iround((_max_y - _min_y) / width_y);
332 if (_nbins_x != nbins_x || _nbins_y != nbins_y) {
333 Deallocate(_bins);
334 Allocate(_bins, _nbins_x, _nbins_y);
335 }
336 _nbins_x = nbins_x;
337 _nbins_y = nbins_y;
338 _width_x = (_max_x - _min_x) / (double)_nbins_x;
339 _width_y = (_max_y - _min_y) / (double)_nbins_y;
340 if ((_nbins_x < 1) || (_nbins_y < 1)) {
341 cerr << "Histogram2D<HistogramType>::PutWidth: Should have at least one bin" << endl;
342 exit(1);
343 }
344 this->Reset();
345 }
346
347 // -----------------------------------------------------------------------------
348 template <class HistogramType>
GetWidth(double * width_x,double * width_y) const349 void Histogram2D<HistogramType>::GetWidth(double *width_x, double *width_y) const
350 {
351 *width_x = _width_x;
352 *width_y = _width_y;
353 }
354
355 // -----------------------------------------------------------------------------
356 template <class HistogramType>
GetWidth(double & width_x,double & width_y) const357 void Histogram2D<HistogramType>::GetWidth(double &width_x, double &width_y) const
358 {
359 width_x = _width_x;
360 width_y = _width_y;
361 }
362
363 // -----------------------------------------------------------------------------
364 template <class HistogramType>
PutNumberOfBins(int nbins_x,int nbins_y)365 void Histogram2D<HistogramType>::PutNumberOfBins(int nbins_x, int nbins_y)
366 {
367 if (_nbins_x != nbins_x || _nbins_y != nbins_y) {
368 Deallocate(_bins);
369 Allocate(_bins, _nbins_x, _nbins_y);
370 }
371 _nbins_x = nbins_x;
372 _nbins_y = nbins_y;
373 _width_x = (_max_x - _min_x) / (double)_nbins_x;
374 _width_y = (_max_y - _min_y) / (double)_nbins_y;
375 if ((_nbins_x < 1) || (_nbins_y < 1)) {
376 cerr << "Histogram2D<HistogramType>::PutWidth: Should have at least one bin" << endl;
377 exit(1);
378 }
379 this->Reset();
380 }
381
382 // -----------------------------------------------------------------------------
383 template <class HistogramType>
PutNumberOfBinsX(int nbins_x)384 void Histogram2D<HistogramType>::PutNumberOfBinsX(int nbins_x)
385 {
386 this->PutNumberOfBins(nbins_x, this->NumberOfBinsY());
387 }
388
389 // -----------------------------------------------------------------------------
390 template <class HistogramType>
PutNumberOfBinsY(int nbins_y)391 void Histogram2D<HistogramType>::PutNumberOfBinsY(int nbins_y)
392 {
393 this->PutNumberOfBins(this->NumberOfBinsX(), nbins_y);
394 }
395
396 // -----------------------------------------------------------------------------
397 template <class HistogramType>
GetNumberOfBins(int * nbins_x,int * nbins_y) const398 void Histogram2D<HistogramType>::GetNumberOfBins(int *nbins_x, int *nbins_y) const
399 {
400 *nbins_x = _nbins_x;
401 *nbins_y = _nbins_y;
402 }
403
404 // -----------------------------------------------------------------------------
405 template <class HistogramType>
GetNumberOfBins(int & nbins_x,int & nbins_y) const406 void Histogram2D<HistogramType>::GetNumberOfBins(int &nbins_x, int &nbins_y) const
407 {
408 nbins_x = _nbins_x;
409 nbins_y = _nbins_y;
410 }
411
412 // -----------------------------------------------------------------------------
413 template <class HistogramType>
AddSample(double x,double y,HistogramType n)414 void Histogram2D<HistogramType>::AddSample(double x, double y, HistogramType n)
415 {
416 if (x < _min_x || x > _max_x || y < _min_y || y > _max_y) return;
417 int i = iround(_nbins_x * (x - _min_x - 0.5*_width_x) / (_max_x - _min_x));
418 int j = iround(_nbins_y * (y - _min_y - 0.5*_width_y) / (_max_y - _min_y));
419 if (i < 0) i = 0;
420 if (j < 0) j = 0;
421 if (i >= _nbins_x) i = _nbins_x - 1;
422 if (j >= _nbins_y) j = _nbins_y - 1;
423 _bins[j][i] += n;
424 _nsamp += n;
425 }
426
427 // -----------------------------------------------------------------------------
428 template <class HistogramType>
DelSample(double x,double y,HistogramType n)429 void Histogram2D<HistogramType>::DelSample(double x, double y, HistogramType n)
430 {
431 if (x < _min_x || x > _max_x || y < _min_y || y > _max_y) return;
432 int i = iround(_nbins_x * (x - _min_x - 0.5*_width_x) / (_max_x - _min_x));
433 int j = iround(_nbins_y * (y - _min_y - 0.5*_width_y) / (_max_y - _min_y));
434 if (i < 0) i = 0;
435 if (j < 0) j = 0;
436 if (i >= _nbins_x) i = _nbins_x - 1;
437 if (j >= _nbins_y) j = _nbins_y - 1;
438 _bins[j][i] -= n;
439 _nsamp -= n;
440 }
441
442 // -----------------------------------------------------------------------------
443 template <class HistogramType>
HistogramX(Histogram1D<HistogramType> & hx) const444 void Histogram2D<HistogramType>::HistogramX(Histogram1D<HistogramType> &hx) const
445 {
446 hx.PutNumberOfBins(_nbins_x);
447 hx.Min(_min_x);
448 hx.Max(_max_x);
449 hx.NumberOfSamples(_nsamp);
450
451 const HistogramType *bin = this->RawPointer();
452 for (int j = 0; j < _nbins_y; ++j) {
453 HistogramType *out = hx.RawPointer();
454 for (int i = 0; i < _nbins_x; ++i, ++bin, ++out) *out += *bin;
455 }
456 }
457
458 // -----------------------------------------------------------------------------
459 template <class HistogramType>
HistogramY(Histogram1D<HistogramType> & hy) const460 void Histogram2D<HistogramType>::HistogramY(Histogram1D<HistogramType> &hy) const
461 {
462 hy.PutNumberOfBins(_nbins_y);
463 hy.Min(_min_y);
464 hy.Max(_max_y);
465 hy.NumberOfSamples(_nsamp);
466
467 const HistogramType *bin = this->RawPointer();
468 HistogramType *out = hy.RawPointer();
469 for (int j = 0; j < _nbins_y; ++j, ++out)
470 for (int i = 0; i < _nbins_x; ++i, ++bin) {
471 *out += *bin;
472 }
473 }
474
475 // -----------------------------------------------------------------------------
476 template <class HistogramType>
Log()477 void Histogram2D<HistogramType>::Log()
478 {
479 LogTransform<HistogramType>::Run(this);
480 }
481
482 // -----------------------------------------------------------------------------
483 template <class HistogramType>
MeanX() const484 double Histogram2D<HistogramType>::MeanX() const
485 {
486 if (_nsamp == 0) {
487 if (debug) {
488 cerr << "Histogram2D<HistogramType>::MeanX: No samples in Histogram" << endl;
489 }
490 return 0;
491 }
492 double tmp, val = .0;
493 for (int i = 0; i < _nbins_x; i++) {
494 tmp = this->BinToValX(i);
495 for (int j = 0; j < _nbins_y; j++) {
496 val += _bins[j][i] * tmp;
497 }
498 }
499 return val / _nsamp;
500 }
501
502 // -----------------------------------------------------------------------------
503 template <class HistogramType>
MeanY() const504 double Histogram2D<HistogramType>::MeanY() const
505 {
506 if (_nsamp == 0) {
507 if (debug) {
508 cerr << "Histogram_2D::MeanY: No samples in Histogram" << endl;
509 }
510 return 0;
511 }
512 double tmp, val = .0;
513 for (int j = 0; j < _nbins_y; j++) {
514 tmp = this->BinToValY(j);
515 for (int i = 0; i < _nbins_x; i++) {
516 val += _bins[j][i] * tmp;
517 }
518 }
519 return val / _nsamp;
520 }
521
522 // -----------------------------------------------------------------------------
523 template <class HistogramType>
VarianceX() const524 double Histogram2D<HistogramType>::VarianceX() const
525 {
526 if (_nsamp == 0) {
527 if (debug) {
528 cerr << "Histogram2D<HistogramType>::VarianceX: No samples in Histogram" << endl;
529 }
530 return 0;
531 }
532 double val = .0;
533 for (int i = 0; i < _nbins_x; i++) {
534 val += this->MarginalProbabilityX(i) * pow(this->BinToValX(i), 2.0);
535 }
536 return val - pow(this->MeanX(), 2.0);
537 }
538
539 // -----------------------------------------------------------------------------
540 template <class HistogramType>
VarianceY() const541 double Histogram2D<HistogramType>::VarianceY() const
542 {
543 if (_nsamp == 0) {
544 if (debug) {
545 cerr << "Histogram2D<HistogramType>::VarianceY: No samples in Histogram" << endl;
546 }
547 return 0;
548 }
549 double val = .0;
550 for (int i = 0; i < _nbins_y; i++) {
551 val += this->MarginalProbabilityY(i) * pow(this->BinToValY(i), 2.0);
552 }
553 return val - pow(this->MeanY(), 2.0);
554 }
555
556 // -----------------------------------------------------------------------------
557 template <class HistogramType>
StandardDeviationX() const558 double Histogram2D<HistogramType>::StandardDeviationX() const
559 {
560 if (_nsamp == 0) {
561 if (debug) {
562 cerr << "Histogram2D<HistogramType>::StandardDeviationX: No samples in Histogram" << endl;
563 }
564 return 0;
565 }
566 return sqrt(this->VarianceX());
567 }
568
569 // -----------------------------------------------------------------------------
570 template <class HistogramType>
StandardDeviationY() const571 double Histogram2D<HistogramType>::StandardDeviationY() const
572 {
573 if (_nsamp == 0) {
574 if (debug) {
575 cerr << "Histogram2D<HistogramType>::StandardDeviationY: No samples in Histogram" << endl;
576 }
577 return 0;
578 }
579 return sqrt(this->VarianceY());
580 }
581
582 // -----------------------------------------------------------------------------
583 template <class HistogramType>
Covariance() const584 double Histogram2D<HistogramType>::Covariance() const
585 {
586 if (_nsamp == 0) {
587 if (debug) {
588 cerr << "Histogram2D<HistogramType>::Covariance: No samples in Histogram" << endl;
589 }
590 return 0;
591 }
592 double val = .0;
593 const double mean_x = this->MeanX();
594 const double mean_y = this->MeanY();
595 for (int j = 0; j < _nbins_y; ++j)
596 for (int i = 0; i < _nbins_x; ++i) {
597 val += _bins[j][i] * (this->BinToValX(i) - mean_x) * (this->BinToValY(j) - mean_y);
598 }
599 return val / _nsamp;
600 }
601
602 // -----------------------------------------------------------------------------
603 template <class HistogramType>
EntropyX() const604 double Histogram2D<HistogramType>::EntropyX() const
605 {
606 if (_nsamp == 0) {
607 if (debug) {
608 cerr << "Histogram2D<HistogramType>::EntropyX: No samples in Histogram" << endl;
609 }
610 return 0;
611 }
612 Histogram1D<HistogramType> hx(0);
613 HistogramX(hx);
614 return hx.Entropy();
615 }
616
617 // -----------------------------------------------------------------------------
618 template <class HistogramType>
EntropyY() const619 double Histogram2D<HistogramType>::EntropyY() const
620 {
621 if (_nsamp == 0) {
622 if (debug) {
623 cerr << "Histogram2D<HistogramType>::EntropyY: No samples in Histogram" << endl;
624 }
625 return 0;
626 }
627 Histogram1D<HistogramType> hy(0);
628 HistogramY(hy);
629 return hy.Entropy();
630 }
631
632 // -----------------------------------------------------------------------------
633 template <class HistogramType>
JointEntropy() const634 double Histogram2D<HistogramType>::JointEntropy() const
635 {
636 if (_nsamp == 0) {
637 if (debug) {
638 cerr << "Histogram2D<HistogramType>::JointEntropy: No samples in Histogram" << endl;
639 }
640 return 0;
641 }
642 // Attention: Parallel summation yielded slightly different results each
643 // time this function was executed. This might be caused by a
644 // different summation of values, which causes different numerical
645 // cancelations. When used for NMI gradient computation, the
646 // registration result could differ from run to run!
647 //
648 // Additional note from 20 Sep 2017 (Andreas):
649 // Changed summation from naive to Kahan summation. This results in identical
650 // joint entropy and NMI values for a symmetric SVFFD registration at the first
651 // iteration, lowest level, when input images are exchanged. With the naive
652 // summation, the joint entropy and therefore NMI values would differ after
653 // about 15 significant digits.
654 double p, s = 0., c = 0., y, t;
655 const HistogramType *bin = _bins[0];
656 const int nbins = _nbins_x * _nbins_y;
657 for (int i = 0; i != nbins; ++i, ++bin) {
658 p = static_cast<double>(*bin);
659 if (p > .0) {
660 y = p * log(p) - c;
661 t = s + y;
662 c = (t - s) - y;
663 s = t;
664 }
665 }
666 // H = - sum (p/n) log(p/n) = log(n) - sum(p log p) / n
667 return log(_nsamp) - s / _nsamp;
668 }
669
670 // -----------------------------------------------------------------------------
671 template <class HistogramType>
ConditionalMeanXY(int i) const672 double Histogram2D<HistogramType>::ConditionalMeanXY(int i) const
673 {
674 double m = .0, p = .0;
675 for (int j = 0; j < _nbins_x; j++) {
676 m += this->JointProbability(j, i) * this->BinToValX(j);
677 p += this->JointProbability(j, i);
678 }
679 return ((p > .0) ? (m / p) : .0);
680 }
681
682 // -----------------------------------------------------------------------------
683 template <class HistogramType>
ConditionalMeanYX(int i) const684 double Histogram2D<HistogramType>::ConditionalMeanYX(int i) const
685 {
686 double m = .0, p = .0;
687 for (int j = 0; j < _nbins_y; j++) {
688 m += this->JointProbability(i, j) * this->BinToValY(j);
689 p += this->JointProbability(i, j);
690 }
691 return ((p > .0) ? (m / p) : .0);
692 }
693
694 // -----------------------------------------------------------------------------
695 template <class HistogramType>
ConditionalVarianceXY(int i) const696 double Histogram2D<HistogramType>::ConditionalVarianceXY(int i) const
697 {
698 double s = .0, p = .0;
699 const double m = this->ConditionalMeanXY(i);
700 for (int j = 0; j < _nbins_x; j++) {
701 s += this->JointProbability(j, i) * pow(this->BinToValX(j) - m, 2);
702 p += this->JointProbability(j, i);
703 }
704 return ((p > .0) ? (s / p) : .0);
705 }
706
707 // -----------------------------------------------------------------------------
708 template <class HistogramType>
ConditionalVarianceYX(int i) const709 double Histogram2D<HistogramType>::ConditionalVarianceYX(int i) const
710 {
711 double s = .0, p = .0;
712 const double m = this->ConditionalMeanYX(i);
713 for (int j = 0; j < _nbins_y; j++) {
714 s += this->JointProbability(i, j) * pow(this->BinToValY(j) - m, 2);
715 p += this->JointProbability(i, j);
716 }
717 return ((p > .0) ? (s / p) : .0);
718 }
719
720 // -----------------------------------------------------------------------------
721 template <class HistogramType>
CorrelationRatioXY() const722 double Histogram2D<HistogramType>::CorrelationRatioXY() const
723 {
724 if (_nsamp == 0) {
725 if (debug) {
726 cerr << "Histogram2D<HistogramType>::CorrelationRatioXY: No samples in Histogram" << endl;
727 }
728 return 0;
729 }
730 double c = 0;
731 const double m = this->MeanX();
732 for (int i = 0; i < _nbins_y; i++) {
733 c += this->MarginalProbabilityY(i) * pow(this->ConditionalMeanXY(i) - m, 2.0);
734 }
735 return (c / this->VarianceX());
736 }
737
738 // -----------------------------------------------------------------------------
739 template <class HistogramType>
CorrelationRatioYX() const740 double Histogram2D<HistogramType>::CorrelationRatioYX() const
741 {
742 if (_nsamp == 0) {
743 if (debug) {
744 cerr << "Histogram2D<HistogramType>::CorrelationRatioYX: No samples in Histogram" << endl;
745 }
746 return 0;
747 }
748 double c = 0;
749 const double m = this->MeanY();
750 for (int i = 0; i < _nbins_x; i++) {
751 c += this->MarginalProbabilityX(i) * pow(this->ConditionalMeanYX(i) - m, 2.0);
752 }
753 return (c / this->VarianceY());
754 }
755
756 // -----------------------------------------------------------------------------
757 template <class HistogramType>
MutualInformation() const758 double Histogram2D<HistogramType>::MutualInformation() const
759 {
760 if (_nsamp == 0) {
761 if (debug) {
762 cerr << "Histogram2D<HistogramType>::MutualInformation: No samples in Histogram" << endl;
763 }
764 return 0;
765 }
766 return this->EntropyX() + this->EntropyY() - this->JointEntropy();
767 }
768
769 // -----------------------------------------------------------------------------
770 template <class HistogramType>
NormalizedMutualInformation() const771 double Histogram2D<HistogramType>::NormalizedMutualInformation() const
772 {
773 if (_nsamp == 0) {
774 if (debug) {
775 cerr << "Histogram2D<HistogramType>::NormalizedMutualInformation: No samples in Histogram" << endl;
776 }
777 return 0;
778 }
779 return (this->EntropyX() + this->EntropyY()) / this->JointEntropy();
780 }
781
782 // -----------------------------------------------------------------------------
783 template <class HistogramType>
CrossCorrelation() const784 double Histogram2D<HistogramType>::CrossCorrelation() const
785 {
786 if (_nsamp == 0) {
787 if (debug) {
788 cerr << "Histogram2D<HistogramType>::CrossCorrelation: No samples in Histogram" << endl;
789 }
790 return 0;
791 }
792 return abs(this->Covariance() / (sqrt(this->VarianceX()) *
793 sqrt(this->VarianceY())));
794 }
795
796 // -----------------------------------------------------------------------------
797 template <class HistogramType>
SumsOfSquaredDifferences() const798 double Histogram2D<HistogramType>::SumsOfSquaredDifferences() const
799 {
800 if (_nsamp == 0) {
801 if (debug) {
802 cerr << "Histogram2D<HistogramType>::SumsOfSquaredDifferences:";
803 cerr << " No samples in Histogram" << endl;
804 }
805 return 0;
806 }
807 double ssd = .0, val_x, val_y = this->BinToValY(0);
808 for (int j = 0; j < _nbins_y; ++j) {
809 val_x = this->BinToValX(0);
810 for (int i = 0; i < _nbins_x; ++i) {
811 ssd += _bins[j][i] * (val_x - val_y) * (val_x - val_y);
812 val_x += _width_x;
813 }
814 val_y += _width_y;
815 }
816 return ssd;
817 }
818
819 // -----------------------------------------------------------------------------
820 template <class HistogramType>
LabelConsistency() const821 double Histogram2D<HistogramType>::LabelConsistency() const
822 {
823 if (_nsamp == 0) {
824 if (debug) {
825 cerr << "Histogram2D<HistogramType>::LabelConsistency: No samples in Histogram" << endl;
826 }
827 return 0;
828 }
829 if (_nbins_x != _nbins_y) {
830 cerr << "Histogram2D<HistogramType>::LabelConsistency: Histogram must have equal number of bins in X and Y" << endl;
831 return 0;
832 }
833 HistogramType n = 0;
834 for (int i = 0; i < _nbins_x; i++) {
835 n += _bins[i][i];
836 }
837 return static_cast<double>(n) / _nsamp;
838 }
839
840 // -----------------------------------------------------------------------------
841 template <class HistogramType>
Kappa() const842 double Histogram2D<HistogramType>::Kappa() const
843 {
844 if (_nsamp == 0) {
845 if (debug) {
846 cerr << "Histogram2D<HistogramType>::Kappa: No samples in Histogram" << endl;
847 }
848 return 0;
849 }
850 if (_nbins_x != _nbins_y) {
851 cerr << "Histogram2D<HistogramType>::Kappa: Histogram must have equal number of bins in X and Y" << endl;
852 return 0;
853 }
854
855 HistogramType *col_sum = new HistogramType[_nbins_x];
856 HistogramType *row_sum = new HistogramType[_nbins_x];
857 for (int j = 0; j < _nbins_x; j++) {
858 col_sum[j] = 0;
859 row_sum[j] = 0;
860 for (int i = 0; i < _nbins_x; i++) {
861 col_sum[j] += _bins[i][j];
862 row_sum[j] += _bins[j][i];
863 }
864 }
865
866 double po = .0, pe = .0;
867 for (int j = 0; j < _nbins_x; j++) {
868 po += _bins[j][j];
869 pe += static_cast<double>(col_sum[j]) * static_cast<double>(row_sum[j]);
870 }
871 po /= _nsamp;
872 pe /= static_cast<double>(_nsamp) * static_cast<double>(_nsamp);
873
874 delete[] row_sum;
875 delete[] col_sum;
876
877 return (po - pe) / (1.0 - pe);
878 }
879
880 // -----------------------------------------------------------------------------
881 template <class HistogramType>
Smooth()882 void Histogram2D<HistogramType>::Smooth()
883 {
884 if (_nsamp == 0) return;
885
886 // Smoothing kernel
887 double kernel[3] = { 1.0/6.0, 2.0/3.0, 1.0/6.0 };
888
889 // Allocate temporary memory
890 double value, **tmp = Allocate<double>(_nbins_x, _nbins_y);
891
892 // Smooth along the x-axis
893 for (int j = 0; j < _nbins_y; j++)
894 for (int i = 0; i < _nbins_x; i++) {
895 value = .0;
896 for (int k = 0; k < 3; k++) {
897 if ((i-1+k >= 0) && (i-1+k < _nbins_x)) {
898 value += kernel[k] * static_cast<double>(_bins[j][i-1+k]);
899 }
900 }
901 tmp[j][i] = value;
902 }
903
904 // Smooth along the y-axis
905 double n = 0., c = 0., y, t;
906 for (int i = 0; i < _nbins_x; i++)
907 for (int j = 0; j < _nbins_y; j++) {
908 value = .0;
909 for (int k = 0; k < 3; k++) {
910 if ((j-1+k >= 0) && (j-1+k < _nbins_y)) {
911 value += kernel[k] * tmp[j-1+k][i];
912 }
913 }
914 _bins[j][i] = static_cast<HistogramType>(value);
915 // Kahan summation of number of samples
916 y = static_cast<double>(value) - c;
917 t = n + y;
918 c = (t - n) - y;
919 n = t;
920 }
921 _nsamp = static_cast<HistogramType>(n);
922
923 // Free tmp memory
924 Deallocate(tmp);
925 }
926
927 // -----------------------------------------------------------------------------
928 template <class HistogramType>
Smoothed(bool pad)929 Histogram2D<HistogramType> Histogram2D<HistogramType>::Smoothed(bool pad)
930 {
931 Histogram2D<HistogramType> hist;
932 if (pad) {
933 const int m = 2; // smoothing kernel size - 1
934 double mx = m * _width_x;
935 double my = m * _width_y;
936 hist.Initialize(_min_x - mx, _max_x + mx, _width_x,
937 _min_y - my, _max_y + my, _width_y);
938 for (int j = 0; j < _nbins_y; ++j)
939 for (int i = 0; i < _nbins_x; ++i) {
940 hist._bins[j + m][i + m] = _bins[j][i];
941 }
942 hist._nsamp = _nsamp;
943 } else {
944 hist.Reset(*this);
945 }
946 hist.Smooth();
947 return hist;
948 }
949
950 // -----------------------------------------------------------------------------
951 template <class HistogramType>
Read(const char * filename)952 void Histogram2D<HistogramType>::Read(const char *filename)
953 {
954 char buffer[255];
955
956 ifstream from(filename);
957 if (!from) {
958 cerr << "Histogram2D<HistogramType>::Read: Can't open file " << filename << endl;
959 exit(1);
960 }
961 if ((_nbins_x > 0) && (_nbins_y > 0)) {
962 Deallocate(_bins);
963 _nbins_x = 0;
964 _nbins_y = 0;
965 }
966
967 from >> buffer;
968 if (strcmp(buffer, "irtkHistogram2D") != 0) {
969 cerr << "Histogram2D<HistogramType>::Read: Invalid format" << endl;
970 exit(1);
971 }
972
973 // Read no. of bins
974 from >> _nbins_x;
975 from >> _nbins_y;
976
977 // Read no. of samples
978 from >> _nsamp;
979
980 // Read min and max of bins
981 from >> _min_x;
982 from >> _max_x;
983 from >> _min_y;
984 from >> _max_y;
985
986 // Read width of bins
987 from >> _width_x;
988 from >> _width_y;
989
990 Allocate(_bins, _nbins_x, _nbins_y);
991 for (int j = 0; j < _nbins_y; j++) {
992 for (int i = 0; i < _nbins_x; i++) {
993 from >> _bins[j][i];
994 }
995 }
996 }
997
998 // -----------------------------------------------------------------------------
999 template <class HistogramType>
Write(const char * filename) const1000 void Histogram2D<HistogramType>::Write(const char *filename) const
1001 {
1002 ofstream to(filename);
1003 if (!to) {
1004 cerr << "Histogram2D<HistogramType>::Write: Can't open file " << filename << endl;
1005 exit(1);
1006 }
1007 to << "irtkHistogram2D\n";
1008 to << _nbins_x << " "
1009 << _nbins_y << " "
1010 << _nsamp << " "
1011 << _min_x << " "
1012 << _max_x << " "
1013 << _min_y << " "
1014 << _max_y << " "
1015 << _width_x << " "
1016 << _width_y << "\n";
1017 for (int j = 0; j < _nbins_y; j++) {
1018 for (int i = 0; i < _nbins_x; i++) {
1019 to << _bins[j][i] << " ";
1020 }
1021 to << "\n";
1022 }
1023 to.close();
1024 }
1025
1026 // -----------------------------------------------------------------------------
1027 template <class HistogramType>
WriteAsImage(const char * filename) const1028 void Histogram2D<HistogramType>::WriteAsImage(const char *filename) const
1029 {
1030 GenericImage<HistogramType> image(_nbins_x, _nbins_y, 1);
1031 for (int j = 0; j < _nbins_y; ++j)
1032 for (int i = 0; i < _nbins_x; ++i) {
1033 image(i, j, 0) = _bins[j][i];
1034 }
1035 image.Write(filename);
1036 }
1037
1038 // -----------------------------------------------------------------------------
1039 template <class HistogramType>
Print() const1040 void Histogram2D<HistogramType>::Print() const
1041 {
1042 cout << _nbins_x << " "
1043 << _nbins_y << " "
1044 << _nsamp << " "
1045 << _min_x << " "
1046 << _max_x << " "
1047 << _min_y << " "
1048 << _max_y << " "
1049 << _width_x << " "
1050 << _width_y
1051 << "\n";
1052 for (int j = 0; j < _nbins_y; j++) {
1053 for (int i = 0; i < _nbins_x; i++) {
1054 cout << _bins[j][i] << " ";
1055 }
1056 cout << "\n";
1057 }
1058 }
1059
1060 // =============================================================================
1061 // Explicit instantiations
1062 // =============================================================================
1063
1064 template class Histogram2D<int>;
1065 template class Histogram2D<double>;
1066
1067
1068 } // namespace mirtk
1069