splineparzenmi.hh
Go to the documentation of this file.
1 /* -*- mia-c++ -*-
2  *
3  * This file is part of MIA - a toolbox for medical image analysis
4  * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
5  *
6  * MIA is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with MIA; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #ifndef mia_core_splineparzenmi_hh
22 #define mia_core_splineparzenmi_hh
23 
24 #include <boost/concept/requires.hpp>
25 #include <boost/concept_check.hpp>
26 #include <mia/core/splinekernel.hh>
27 #include <mia/core/histogram.hh>
28 
30 
45 public:
55  CSplineParzenMI(size_t rbins, PSplineKernel rkernel,
56  size_t mbins, PSplineKernel mkernel, double cut_histogram);
57 
58 
69  template <typename MovIterator, typename RefIterator>
70  BOOST_CONCEPT_REQUIRES( ((::boost::ForwardIterator<MovIterator>))
71  ((::boost::ForwardIterator<RefIterator>)),
72  (void)
73  )
74  fill(MovIterator mov_begin, MovIterator mov_end,
75  RefIterator ref_begin, RefIterator ref_end);
76 
77 
91  template <typename MovIterator, typename RefIterator, typename MaskIterator>
92  void fill(MovIterator mov_begin, MovIterator mov_end,
93  RefIterator ref_begin, RefIterator ref_end,
94  MaskIterator mask_begin, MaskIterator mask_end);
95 
96 
101  double value() const;
102 
110  double get_gradient(double moving, double reference) const;
111 
119  double get_gradient_slow(double moving, double reference) const;
123  void reset();
124 protected:
126  void fill_histograms(const std::vector<double>& values,
127  double rmin, double rmax,
128  double mmin, double mmax);
129 private:
130 
131  double scale_moving(double x) const;
132  double scale_reference(double x) const;
133 
134  void evaluate_histograms();
135  void evaluate_log_cache();
136 
137  size_t m_ref_bins;
138  PSplineKernel m_ref_kernel;
139  size_t m_ref_border;
140  size_t m_ref_real_bins;
141  double m_ref_max;
142  double m_ref_min;
143  double m_ref_scale;
144 
145  size_t m_mov_bins;
146 
147  PSplineKernel m_mov_kernel;
148  size_t m_mov_border;
149  size_t m_mov_real_bins;
150  double m_mov_max;
151  double m_mov_min;
152  double m_mov_scale;
153 
154 
155  std::vector<double> m_joined_histogram;
156  std::vector<double> m_ref_histogram;
157  std::vector<double> m_mov_histogram;
158 
159  std::vector<std::vector<double> > m_pdfLogCache;
160  double m_cut_histogram;
161 
162  template <typename Iterator>
163  std::pair<double,double> get_reduced_range(Iterator begin, Iterator end)const;
164 
165  template <typename DataIterator, typename MaskIterator>
166  std::pair<double,double>
167  get_reduced_range_masked(DataIterator dbegin,
168  DataIterator dend,
169  MaskIterator mbegin)const;
170 
171 };
172 
173 template <typename MovIterator, typename RefIterator>
174 BOOST_CONCEPT_REQUIRES( ((::boost::ForwardIterator<MovIterator>))
175  ((::boost::ForwardIterator<RefIterator>)),
176  (void)
177  )
178  CSplineParzenMI::fill(MovIterator mov_begin, MovIterator mov_end,
179  RefIterator ref_begin, RefIterator ref_end)
180 {
181  std::fill(m_joined_histogram.begin(), m_joined_histogram.end(), 0.0);
182 
183  assert(mov_begin != mov_end);
184  assert(ref_begin != ref_end);
185 
186  if (m_mov_max < m_mov_min) {
187  // (re)evaluate the ranges
188  auto mov_range = get_reduced_range(mov_begin, mov_end);
189  if (mov_range.second == mov_range.first)
190  throw std::invalid_argument("relevant moving image intensity range is zero");
191  m_mov_min = mov_range.first;
192  m_mov_max = mov_range.second;
193  m_mov_scale = (m_mov_bins - 1) / (m_mov_max - m_mov_min);
194  cvdebug() << "Mov Range = [" << m_mov_min << ", " << m_mov_max << "]\n";
195  }
196 
197 
198  if (m_ref_max < m_ref_min) {
199  auto ref_range = get_reduced_range(ref_begin, ref_end);
200  if (ref_range.second == ref_range.first)
201  throw std::invalid_argument("relevant reference image intensity range is zero");
202 
203  m_ref_min = ref_range.first;
204  m_ref_max = ref_range.second;
205  m_ref_scale = (m_ref_bins - 1) / (m_ref_max - m_ref_min);
206  cvdebug() << "Ref Range = [" << m_ref_min << ", " << m_ref_max << "]\n";
207  }
208 
209 
210  std::vector<double> mweights(m_mov_kernel->size());
211  std::vector<double> rweights(m_ref_kernel->size());
212 
213  size_t N = 0;
214  while (ref_begin != ref_end && mov_begin != mov_end) {
215 
216  const double mov = scale_moving(*mov_begin);
217  const double ref = scale_reference(*ref_begin);
218 
219  const int mov_start = m_mov_kernel->get_start_idx_and_value_weights(mov, mweights) + m_mov_border;
220  const int ref_start = m_ref_kernel->get_start_idx_and_value_weights(ref, rweights) + m_ref_border;
221 
222  for (size_t r = 0; r < m_ref_kernel->size(); ++r) {
223  auto inbeg = m_joined_histogram.begin() +
224  m_mov_real_bins * (ref_start + r) + mov_start;
225  double rw = rweights[r];
226  std::transform(mweights.begin(), mweights.end(), inbeg, inbeg,
227  [rw](double mw, double jhvalue){ return mw * rw + jhvalue;});
228  }
229 
230  ++N;
231  ++mov_begin;
232  ++ref_begin;
233  }
234 
235  cvdebug() << "CSplineParzenMI::fill: counted " << N << " pixels\n";
236  // normalize joined histogram
237  const double nscale = 1.0/N;
238  transform(m_joined_histogram.begin(), m_joined_histogram.end(), m_joined_histogram.begin(),
239  [&nscale](double jhvalue){return jhvalue * nscale;});
240 
241  evaluate_histograms();
242  evaluate_log_cache();
243 }
244 
245 
246 template <typename MovIterator, typename RefIterator, typename MaskIterator>
247 void CSplineParzenMI::fill(MovIterator mov_begin, MovIterator mov_end,
248  RefIterator ref_begin, RefIterator ref_end,
249  MaskIterator mask_begin, MaskIterator mask_end)
250 {
251  std::fill(m_joined_histogram.begin(), m_joined_histogram.end(), 0.0);
252 
253  assert(mov_begin != mov_end);
254  assert(ref_begin != ref_end);
255 
256  // no mask
257  if (mask_begin == mask_end)
258  fill(mov_begin, mov_end, ref_begin, ref_end);
259 
260  if (m_mov_max < m_mov_min) {
261  // (re)evaluate the ranges
262 
263  auto mov_range = get_reduced_range_masked(mov_begin, mov_end, mask_begin);
264  if (mov_range.second == mov_range.first)
265  throw std::invalid_argument("relevant moving image intensity range is zero");
266  m_mov_min = mov_range.first;
267  m_mov_max = mov_range.second;
268  m_mov_scale = (m_mov_bins - 1) / (m_mov_max - m_mov_min);
269  cvdebug() << "Mov Range = [" << m_mov_min << ", " << m_mov_max << "]\n";
270  }
271 
272 
273  if (m_ref_max < m_ref_min) {
274 
275  auto ref_range = get_reduced_range_masked(ref_begin, ref_end, mask_begin);
276  if (ref_range.second == ref_range.first)
277  throw std::invalid_argument("relevant reference image intensity range is zero");
278 
279  m_ref_min = ref_range.first;
280  m_ref_max = ref_range.second;
281  m_ref_scale = (m_ref_bins - 1) / (m_ref_max - m_ref_min);
282  cvdebug() << "Ref Range = [" << m_ref_min << ", " << m_ref_max << "]\n";
283  }
284 
285 
286  std::vector<double> mweights(m_mov_kernel->size());
287  std::vector<double> rweights(m_ref_kernel->size());
288 
289  size_t N = 0;
290  while (ref_begin != ref_end && mov_begin != mov_end) {
291 
292  if (*mask_begin) {
293  const double mov = scale_moving(*mov_begin);
294  const double ref = scale_reference(*ref_begin);
295 
296  const int mov_start = m_mov_kernel->get_start_idx_and_value_weights(mov, mweights) + m_mov_border;
297  const int ref_start = m_ref_kernel->get_start_idx_and_value_weights(ref, rweights) + m_ref_border;
298 
299  for (size_t r = 0; r < m_ref_kernel->size(); ++r) {
300  auto inbeg = m_joined_histogram.begin() +
301  m_mov_real_bins * (ref_start + r) + mov_start;
302  double rw = rweights[r];
303  std::transform(mweights.begin(), mweights.end(), inbeg, inbeg,
304  [rw](double mw, double jhvalue){ return mw * rw + jhvalue;});
305  }
306 
307  ++N;
308  }
309  ++mask_begin;
310  ++mov_begin;
311  ++ref_begin;
312  }
313 
314  cvdebug() << "CSplineParzenMI::fill: counted " << N << " pixels\n";
315  // normalize joined histogram
316  const double nscale = 1.0/N;
317  transform(m_joined_histogram.begin(), m_joined_histogram.end(), m_joined_histogram.begin(),
318  [&nscale](double jhvalue){return jhvalue * nscale;});
319 
320  evaluate_histograms();
321  evaluate_log_cache();
322 }
323 
324 template <typename Iterator>
325 std::pair<double,double> CSplineParzenMI::get_reduced_range(Iterator begin, Iterator end)const
326 {
327  auto range = std::minmax_element(begin, end);
329  THistogram<Feeder> h(Feeder(*range.first, *range.second, 4096));
330  h.push_range(begin, end);
331  auto reduced_range = h.get_reduced_range(m_cut_histogram);
332  cvinfo() << "CSplineParzenMI: reduce range by "<< m_cut_histogram
333  <<"% from [" << *range.first << ", " << *range.second
334  << "] to [" << reduced_range.first << ", " << reduced_range.second << "]\n";
335  return std::make_pair(reduced_range.first, reduced_range.second);
336 
337 }
338 
339 template <typename DataIterator, typename MaskIterator>
340 std::pair<double,double>
341 CSplineParzenMI::get_reduced_range_masked(DataIterator dbegin,
342  DataIterator dend,
343  MaskIterator mbegin)const
344 {
345  auto ib = dbegin;
346  auto im = mbegin;
347 
348  while (! *im && ib != dend) {
349  ++im;
350  ++ib;
351  }
352 
353  if (ib == dend)
354  throw std::runtime_error("CSplineParzenMI: empty mask");
355 
356  double range_max = *ib;
357  double range_min = *ib;
358  ++ib; ++im;
359 
360  while (ib != dend) {
361  if (*im) {
362  if (*ib < range_min)
363  range_min = *ib;
364  if (*ib > range_max)
365  range_max = *ib;
366  }
367  ++ib;
368  ++im;
369  }
370 
371 
373  THistogram<Feeder> h(Feeder(range_min, range_max, 4096));
374 
375  ib = dbegin;
376  im = mbegin;
377  while (ib != dend) {
378  if (*im)
379  h.push(*ib);
380  ++ib;
381  ++im;
382  }
383 
384  auto reduced_range = h.get_reduced_range(m_cut_histogram);
385  cvinfo() << "CSplineParzenMI: reduce range by "<< m_cut_histogram
386  << "% from [" << range_min << ", " << range_max
387  << "] to [" << reduced_range.first << ", " << reduced_range.second << "]\n";
388  return std::make_pair(reduced_range.first, reduced_range.second);
389 
390 }
391 
393 #endif
vstream & cvinfo()
informal output that may be of interest to understand problems with a program and are of higher prior...
Definition: msgstream.hh:252
CDebugSink & cvdebug()
Definition: msgstream.hh:216
void fill(MovIterator mov_begin, MovIterator mov_end, RefIterator ref_begin, RefIterator ref_end)
range_type get_reduced_range(double remove) const
Definition: histogram.hh:473
#define NS_MIA_BEGIN
conveniance define to start the mia namespace
Definition: defines.hh:33
Implementation of mutual information based on B-splines.
a simple histogram that uses an instance of THistogramFeeder as input converter
Definition: histogram.hh:132
std::shared_ptr< CSplineKernel > PSplineKernel
EXPORT_2D C2DFVectorfield get_gradient(const C2DImage &image)
A class to normalize and quantizize input data to a given histogram range with its given number of bi...
Definition: histogram.hh:49
void push(typename Feeder::value_type x)
Definition: histogram.hh:319
#define EXPORT_CORE
Macro to manage Visual C++ style dllimport/dllexport.
Definition: defines.hh:101
void push_range(Iterator begin, Iterator end)
Definition: histogram.hh:327
#define NS_MIA_END
conveniance define to end the mia namespace
Definition: defines.hh:36