1 /*
2  * Medical Image Registration ToolKit (MIRTK)
3  *
4  * Copyright 2008-2015 Imperial College London
5  * Copyright 2008-2015 Daniel Rueckert, Julia Schnabel
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #include "mirtk/Histogram1D.h"
21 
22 #include "mirtk/Math.h"
23 #include "mirtk/Memory.h"
24 #include "mirtk/Algorithm.h"
25 
26 #include "mirtk/CommonExport.h"
27 
28 
29 namespace mirtk {
30 
31 
32 // Global "debug" flag (cf. mirtk/Options.h)
33 MIRTK_Common_EXPORT extern int debug;
34 
35 
36 // =============================================================================
37 // Histogram1D
38 // =============================================================================
39 
40 // -----------------------------------------------------------------------------
41 template <class HistogramType>
Histogram1D(const Histogram1D & h)42 Histogram1D<HistogramType>::Histogram1D(const Histogram1D &h)
43 :
44   Object(h)
45 {
46   _min   = h._min;
47   _max   = h._max;
48   _width = h._width;
49   _nbins = h._nbins;
50   _nsamp = h._nsamp;
51   if (_nbins > 0) {
52     Allocate(_bins, _nbins);
53     memcpy(RawPointer(), h.RawPointer(), _nbins * sizeof(HistogramType));
54   } else {
55     _bins = nullptr;
56   }
57 }
58 
59 // -----------------------------------------------------------------------------
60 template <class HistogramType>
Histogram1D(int nbins)61 Histogram1D<HistogramType>::Histogram1D(int nbins)
62 {
63   _min   = 0;
64   _max   = nbins;
65   _width = 1;
66   _nbins = nbins;
67   _nsamp = 0;
68   if (nbins > 0) {
69     CAllocate(_bins, _nbins);
70   } else {
71     _bins = nullptr;
72   }
73 }
74 
75 // -----------------------------------------------------------------------------
76 template <class HistogramType>
Histogram1D(double min,double max,double width)77 Histogram1D<HistogramType>::Histogram1D(double min, double max, double width)
78 {
79   _min   = min;
80   _max   = max;
81   _nbins = static_cast<int>((_max - _min) / width);
82   _width = static_cast<double>(_max - _min) / _nbins;
83   _nsamp = 0;
84   if (_nbins < 1) {
85     cerr << "Histogram1D::Histogram1D: Should have at least one bin" << endl;
86     exit(1);
87   }
88   CAllocate(_bins, _nbins);
89 }
90 
91 // -----------------------------------------------------------------------------
92 template <class HistogramType>
Histogram1D(const char * filename)93 Histogram1D<HistogramType>::Histogram1D(const char *filename)
94 {
95   ifstream from(filename);
96   if (!from) {
97     cerr << "Histogram1D::Read: Can't open file " << filename << endl;
98     exit(1);
99   }
100   char buffer[255];
101   from >> buffer;
102   if (strcmp(buffer, "irtkHistogram1D") != 0) {
103     cerr << "Histogram1D::Read: Invalid format" << endl;
104     exit(1);
105   }
106   from >> _nbins >> _nsamp >> _min >> _max >> _width;
107   Allocate(_bins, _nbins);
108   for (int i = 0; i < _nbins; ++i) from >> _bins[i];
109 }
110 
111 // -----------------------------------------------------------------------------
112 template <class HistogramType>
113 Histogram1D<HistogramType> &Histogram1D<HistogramType>
operator =(const Histogram1D<HistogramType> & other)114 ::operator =(const Histogram1D<HistogramType> &other)
115 {
116   if (this != &other) {
117     _nbins = other._nbins;
118     _nsamp = other._nsamp;
119     _min   = other._min;
120     _max   = other._max;
121     _width = other._width;
122     if (other._bins != _bins) {
123       Deallocate(_bins);
124       Allocate(_bins, _nbins);
125       memcpy(_bins, other._bins, _nbins * sizeof(HistogramType));
126     }
127   }
128   return *this;
129 }
130 
131 // -----------------------------------------------------------------------------
132 template <class HistogramType>
~Histogram1D()133 Histogram1D<HistogramType>::~Histogram1D()
134 {
135   Deallocate(_bins);
136   _nbins = 0;
137   _nsamp = 0;
138   _min   = 0;
139   _max   = 0;
140   _width = 0;
141 }
142 
143 // -----------------------------------------------------------------------------
144 template <class HistogramType>
Reset()145 void Histogram1D<HistogramType>::Reset()
146 {
147   memset(_bins, 0, _nbins * sizeof(HistogramType));
148   _nsamp = 0;
149 }
150 
151 // -----------------------------------------------------------------------------
152 template <class HistogramType>
PutMin(double min)153 void Histogram1D<HistogramType>::PutMin(double min)
154 {
155   Min(min);
156   this->Reset();
157 }
158 
159 // -----------------------------------------------------------------------------
160 template <class HistogramType>
PutMax(double max)161 void Histogram1D<HistogramType>::PutMax(double max)
162 {
163   Max(max);
164   this->Reset();
165 }
166 
167 // -----------------------------------------------------------------------------
168 template <class HistogramType>
PutWidth(double width)169 void Histogram1D<HistogramType>::PutWidth(double width)
170 {
171   const int nbins = iround((_max - _min) / width);
172   if (nbins < 1) {
173     cerr << "Histogram1D::PutWidth: Should have at least one bin" << endl;
174     exit(1);
175   }
176   if (_nbins != nbins) {
177     Deallocate(_bins);
178     Allocate(_bins, nbins);
179     _nbins = nbins;
180   }
181   _width = (_max - _min) / _nbins;
182   this->Reset();
183 }
184 
185 // -----------------------------------------------------------------------------
186 template <class HistogramType>
PutNumberOfBins(int nbins)187 void Histogram1D<HistogramType>::PutNumberOfBins(int nbins)
188 {
189   if (nbins < 1) {
190     cerr << "Histogram1D::PutNumberOfBins: Should have at least one bin" << endl;
191     exit(1);
192   }
193   NumberOfBins(nbins);
194   this->Reset();
195 }
196 
197 // -----------------------------------------------------------------------------
198 template <class HistogramType>
Log()199 void Histogram1D<HistogramType>::Log()
200 {
201   const HistogramType zero(0);
202   if (_nsamp == zero) {
203     if (debug) {
204       cerr << "Histogram1D::Log: No samples in Histogram" << endl;
205     }
206     return;
207   }
208   for (int i = 0; i < _nbins; ++i) {
209     if (_bins[i] > zero) {
210       _bins[i] = static_cast<HistogramType>(log(static_cast<double>(_bins[i]) / static_cast<double>(_nsamp)));
211     } else {
212       _bins[i] = zero;
213     }
214   }
215 }
216 
217 // -----------------------------------------------------------------------------
218 template <class HistogramType>
Smooth()219 void Histogram1D<HistogramType>::Smooth()
220 {
221   if (_nsamp == 0) {
222     if (debug) {
223       cerr << "Histogram1D::Smooth: No samples in Histogram" << endl;
224     }
225     return;
226   }
227   if (_nbins == 0) return;
228 
229   // Smoothing kernel
230   const double kernel[3] = { 1.0 / 6.0, 2.0 / 3.0, 1.0 / 6.0 };
231 
232   // Allocate new histogram
233   HistogramType *bins = Allocate<HistogramType>(_nbins);
234 
235   // Smooth histogram
236   if (_nbins > 1) {
237     bins[0] = static_cast<HistogramType>(kernel[1] * static_cast<double>(_bins[0])
238                                        + kernel[0] * static_cast<double>(_bins[1]));
239   }
240   else {
241     bins[0] = static_cast<HistogramType>(kernel[1] * static_cast<double>(_bins[0]));
242   }
243   for (int i = 1; i < _nbins - 1; ++i) {
244     bins[i] = static_cast<HistogramType>(kernel[2] * static_cast<double>(_bins[i - 1])
245                                        + kernel[1] * static_cast<double>(_bins[i])
246                                        + kernel[0] * static_cast<double>(_bins[i + 1]));
247   }
248   if (_nbins > 1) {
249     bins[_nbins - 1] = static_cast<HistogramType>(kernel[1] * static_cast<double>(_bins[_nbins - 1])
250                                                 + kernel[2] * static_cast<double>(_bins[_nbins - 2]));
251   }
252 
253   // Replace histogram by smoothed version
254   Deallocate(_bins);
255   _bins = bins;
256 }
257 
258 // -----------------------------------------------------------------------------
259 template <class HistogramType>
Mode() const260 double Histogram1D<HistogramType>::Mode() const
261 {
262   if (_nsamp > 0) {
263     HistogramType max_value = 0;
264     for (int i = 0; i < _nbins; ++i) {
265       max_value = max(max_value, _bins[i]);
266     }
267     for (int i = 0; i < _nbins; ++i) {
268       if (_bins[i] >= max_value) {
269         return this->BinToVal(i);
270       }
271     }
272   } else {
273     if (debug) {
274       cerr << "Histogram1D::Mode: No samples in Histogram" << endl;
275     }
276   }
277   return NaN;
278 }
279 
280 // -----------------------------------------------------------------------------
281 template <class HistogramType>
Modes() const282 Array<double> Histogram1D<HistogramType>::Modes() const
283 {
284   Array<double> modes;
285   if (_nsamp == 0) {
286     if (debug) {
287       cerr << "Histogram1D::Modes: No samples in Histogram" << endl;
288     }
289     return modes;
290   }
291   HistogramType max_value = 0;
292   for (int i = 0; i < _nbins; ++i) {
293     max_value = max(max_value, _bins[i]);
294   }
295   modes.reserve(10);
296   for (int i = 0; i < _nbins; ++i) {
297     if (_bins[i] >= max_value) {
298       modes.push_back(this->BinToVal(i));
299     }
300   }
301   modes.shrink_to_fit();
302   Sort(modes);
303   return modes;
304 }
305 
306 // -----------------------------------------------------------------------------
307 template <class HistogramType>
Mean() const308 double Histogram1D<HistogramType>::Mean() const
309 {
310   if (_nsamp == 0) {
311     if (debug) {
312       cerr << "Histogram1D::Mean: No samples in Histogram" << endl;
313     }
314     return 0;
315   }
316   double val = 0;
317   for (int i = 0; i < _nbins; ++i) {
318     val += _bins[i] * this->BinToVal(i);
319   }
320   return val / _nsamp;
321 }
322 
323 // -----------------------------------------------------------------------------
324 template <class HistogramType>
Variance() const325 double Histogram1D<HistogramType>::Variance() const
326 {
327   if (_nsamp == 0) {
328     if (debug) {
329       cerr << "Histogram1D::Variance: No samples in Histogram" << endl;
330     }
331     return 0;
332   }
333   double val = 0, mean = this->Mean();
334   for (int i = 0; i < _nbins; ++i) {
335     val += _bins[i] * (this->BinToVal(i) - mean) * (this->BinToVal(i) - mean);
336   }
337   return val / _nsamp;
338 }
339 
340 // -----------------------------------------------------------------------------
341 template <class HistogramType>
StandardDeviation() const342 double Histogram1D<HistogramType>::StandardDeviation() const
343 {
344   return sqrt(this->Variance());
345 }
346 
347 // -----------------------------------------------------------------------------
348 template <class HistogramType>
Entropy() const349 double Histogram1D<HistogramType>::Entropy() const
350 {
351   if (_nsamp == 0) {
352     if (debug) {
353       cerr << "Histogram1D::Entropy: No samples in Histogram" << endl;
354     }
355     return 0;
356   }
357   // Attention: Parallel summation yielded slightly different results each
358   //            time this function was executed. This might be caused by a
359   //            different summation of values, which causes different numerical
360   //            cancelations. Qhen used for NMI gradient computation, the
361   //            registration result could differ from run to run!
362   double p, sum = .0;
363   const HistogramType *bin = _bins;
364   for (int i = 0; i != _nbins; ++i, ++bin) {
365     p = static_cast<double>(*bin);
366     if (p > .0) sum += p * log(p);
367   }
368   // H = - sum (p/n) log(p/n) = log(n) - sum(p log p) / n
369   return log(_nsamp) - sum / _nsamp;
370 }
371 
372 // -----------------------------------------------------------------------------
373 template <class HistogramType>
Read(const char * filename)374 void Histogram1D<HistogramType>::Read(const char *filename)
375 {
376   ifstream from(filename);
377   if (!from) {
378     cerr << "Histogram1D::Read: Can't open file " << filename << endl;
379     exit(1);
380   }
381 
382   char buffer[255];
383   from >> buffer;
384   if (strcmp(buffer, "Histogram1D") != 0) {
385     cerr << "Histogram1D::Read: Invalid format" << endl;
386     exit(1);
387   }
388 
389   int nbins;
390   from >> nbins >> _nsamp >> _min >> _max >> _width;
391   if (_nbins != nbins) {
392     Deallocate(_bins);
393     Allocate(_bins, nbins);
394     _nbins = nbins;
395   }
396   for (int i = 0; i < _nbins; ++i) from >> _bins[i];
397 }
398 
399 // -----------------------------------------------------------------------------
400 template <class HistogramType>
Write(const char * filename) const401 void Histogram1D<HistogramType>::Write(const char *filename) const
402 {
403   ofstream to(filename);
404   if (!to) {
405     cerr << "Histogram1D::Write: Can't open file " << filename << endl;
406     exit(1);
407   }
408   to << "irtkHistogram1D\n";
409   to << _nbins << " " << _nsamp << " " << _min << " " << _max << " " << _width << "\n";
410   for (int i = 0; i < _nbins; ++i) to << _bins[i] << "\n";
411   to.close();
412 }
413 
414 // -----------------------------------------------------------------------------
415 template <class HistogramType>
Print() const416 void Histogram1D<HistogramType>::Print() const
417 {
418   cout << _nbins << " " << _nsamp << " " << _min << " " << _max << " " << _width << "\n";
419   for (int i = 0; i < _nbins; ++i) cout << _bins[i] << "\n";
420 }
421 
422 // =============================================================================
423 // Explicit instantiations
424 // =============================================================================
425 
426 template class Histogram1D<int>;
427 template class Histogram1D<double>;
428 template class Histogram1D<unsigned char>;
429 template class Histogram1D<short>;
430 template class Histogram1D<unsigned short>;
431 template class Histogram1D<float>;
432 
433 
434 } // namespace mirtk
435