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