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