LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
Statistics.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
33 #include <iostream>
34 #include <limits>
35 #include <cmath>
36 #include <cassert>
37 #include "boost/shared_ptr.hpp"
38 #include "lsst/pex/exceptions.h"
39 #include "lsst/afw/image/Image.h"
41 #include "lsst/utils/ieee.h"
42 #include "lsst/afw/geom/Angle.h"
43 
44 using namespace std;
45 namespace afwImage = lsst::afw::image;
46 namespace afwMath = lsst::afw::math;
47 namespace afwGeom = lsst::afw::geom;
48 namespace pexExceptions = lsst::pex::exceptions;
49 
50 namespace {
51  double const NaN = std::numeric_limits<double>::quiet_NaN();
52  double const MAX_DOUBLE = std::numeric_limits<double>::max();
53  double const IQ_TO_STDEV = 0.741301109252802; // 1 sigma in units of iqrange (assume Gaussian)
54 
58  class AlwaysTrue {
59  public:
60  template<typename T>
61  bool operator()(T) const {
62  return true;
63  }
64  template<typename Ta, typename Tb>
65  bool operator()(Ta, Tb) const {
66  return true;
67  }
68  template<typename Ta, typename Tb, typename Tc>
69  bool operator()(Ta, Tb, Tc) const {
70  return true;
71  }
72  };
73 
77  class AlwaysFalse {
78  public:
79  template<typename T>
80  bool operator()(T) const {
81  return false;
82  }
83  template<typename Ta, typename Tb>
84  bool operator()(Ta, Tb) const {
85  return false;
86  }
87  template<typename Ta, typename Tb, typename Tc>
88  bool operator()(Ta, Tb, Tc) const {
89  return false;
90  }
91  };
92 
96  class CheckFinite {
97  public:
98  template<typename T>
99  bool operator()(T val) const {
100  return lsst::utils::isfinite(static_cast<float>(val));
101  }
102  };
103 
107  class CheckValueLtMin {
108  public:
109  template<typename Tval, typename Tmin>
110  bool operator()(Tval val, Tmin min) const {
111  return (static_cast<Tmin>(val) < min);
112  }
113  };
114 
118  class CheckValueGtMax {
119  public:
120  template<typename Tval, typename Tmax>
121  bool operator()(Tval val, Tmax max) const {
122  return (static_cast<Tmax>(val) > max);
123  }
124  };
125 
129  class CheckClipRange {
130  public:
131  template<typename Tval, typename Tcen, typename Tmax>
132  bool operator()(Tval val, Tcen center, Tmax cliplimit) const {
133  Tmax tmp = fabs(val - center);
134  return (tmp <= cliplimit);
135  }
136  };
137 
138  // define some abbreviated typenames for the test templates
139  typedef CheckFinite ChkFin;
140  typedef CheckValueLtMin ChkMin;
141  typedef CheckValueGtMax ChkMax;
142  typedef CheckClipRange ChkClip;
143  typedef AlwaysTrue AlwaysT;
144  typedef AlwaysFalse AlwaysF;
145 
146  // Return the variance of a variance, assuming a Gaussian
147  // There is apparently an attempt to correct for bias in the factor (n - 1)/n. RHL
148  inline double varianceError(double const variance, int const n)
149  {
150  return 2*(n - 1)*variance*variance/static_cast<double>(n*n);
151  }
152 
153  /*********************************************************************************************************/
154  // return type for processPixels
155  typedef boost::tuple<int, // n
156  double, // sum
158  afwMath::Statistics::Value, // variance
159  double, // min
160  double, // max
161  lsst::afw::image::MaskPixel // allPixelOrMask
162  > StandardReturn;
163 
164  /*
165  * Functions which convert the booleans into calls to the proper templated types, one type per
166  * recursion level
167  */
168  /*
169  * This function handles the inner summation loop, with tests templated
170  *
171  * The idea here is to allow different conditionals in the inner loop, but avoid repeating code.
172  * Each test is actually a functor which is handled through a template. If the
173  * user requests a test (eg check for NaNs), the function is instantiated with the appropriate functor.
174  * Otherwise, an 'AlwaysTrue' or 'AlwaysFalse' object is passed in. The compiler then compiles-out
175  * a test which is always false, or removes the conditional for a test which is always true.
176  */
177  template<typename IsFinite,
178  typename HasValueLtMin,
179  typename HasValueGtMax,
180  typename InClipRange,
181  bool useWeights,
182  typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
183  StandardReturn processPixels(ImageT const &img,
184  MaskT const &msk,
185  VarianceT const &var,
186  WeightT const &weights,
187  int const,
188  int const nCrude,
189  int const stride,
190  double const meanCrude,
191  double const cliplimit,
192  bool const weightsAreMultiplicative,
193  int const andMask,
194  bool const calcErrorFromInputVariance
195  )
196  {
197  int n = 0;
198  double sumw = 0.0; // sum(weight) (N.b. weight will be 1.0 if !useWeights)
199  double sumw2 = 0.0; // sum(weight^2)
200  double sumx = 0; // sum(data*weight)
201  double sumx2 = 0; // sum(data*weight^2)
202 #if 1
203  double sumvw2 = 0.0; // sum(variance*weight^2)
204 #endif
205  double min = (nCrude) ? meanCrude : MAX_DOUBLE;
206  double max = (nCrude) ? meanCrude : -MAX_DOUBLE;
207 
208  afwImage::MaskPixel allPixelOrMask = 0x0;
209 
210  for (int iY = 0; iY < img.getHeight(); iY += stride) {
211 
212  typename MaskT::x_iterator mptr = msk.row_begin(iY);
213  typename VarianceT::x_iterator vptr = var.row_begin(iY);
214  typename WeightT::x_iterator wptr = weights.row_begin(iY);
215 
216  for (typename ImageT::x_iterator ptr = img.row_begin(iY), end = ptr + img.getWidth();
217  ptr != end; ++ptr, ++mptr, ++vptr, ++wptr) {
218 
219  if (IsFinite()(*ptr) && !(*mptr & andMask) &&
220  InClipRange()(*ptr, meanCrude, cliplimit) ) { // clip
221 
222  double const delta = (*ptr - meanCrude);
223 
224  if (useWeights) {
225  double weight = *wptr;
226  if (weightsAreMultiplicative) {
227  ;
228  } else {
229  if (*wptr <= 0) {
230  continue;
231  }
232  weight = 1/weight;
233  }
234 
235  sumw += weight;
236  sumw2 += weight*weight;
237  sumx += weight*delta;
238  sumx2 += weight*delta*delta;
239 
240  if (calcErrorFromInputVariance) {
241  double const var = *vptr;
242  sumvw2 += var*weight*weight;
243  }
244  } else {
245  sumx += delta;
246  sumx2 += delta*delta;
247 
248  if (calcErrorFromInputVariance) {
249  double const var = *vptr;
250  sumvw2 += var;
251  }
252  }
253 
254  allPixelOrMask |= *mptr;
255 
256  if (HasValueLtMin()(*ptr, min)) { min = *ptr; }
257  if (HasValueGtMax()(*ptr, max)) { max = *ptr; }
258  n++;
259  }
260  }
261  }
262  if (n == 0) {
263  min = NaN;
264  max = NaN;
265  }
266 
267  // estimate of population mean and variance.
268  double mean, variance;
269  if (!useWeights) {
270  sumw = sumw2 = n;
271  }
272 
273  // N.b. if sumw == 0 or sumw*sumw == sumw2 (e.g. n == 1) we'll get NaNs
274  // N.b. the estimator of the variance assumes that the sample points all have the same variance;
275  // otherwise, what is it that we're estimating?
276  mean = sumx/sumw;
277  variance = sumx2/sumw - ::pow(mean, 2); // biased estimator
278  variance *= sumw*sumw/(sumw*sumw - sumw2); // debias
279 
280  double meanVar; // (standard error of mean)^2
281  if (calcErrorFromInputVariance) {
282  meanVar = sumvw2/(sumw*sumw);
283  } else {
284  meanVar = variance*sumw2/(sumw*sumw);
285  }
286 
287  double varVar = varianceError(variance, n); // error in variance; incorrect if useWeights is true
288 
289  sumx += sumw*meanCrude;
290  mean += meanCrude;
291 
292  return StandardReturn(n, sumx,
293  afwMath::Statistics::Value(mean, meanVar),
294  afwMath::Statistics::Value(variance, varVar), min, max, allPixelOrMask);
295  }
296 
297  template<typename IsFinite,
298  typename HasValueLtMin,
299  typename HasValueGtMax,
300  typename InClipRange,
301  bool useWeights,
302  typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
303  StandardReturn processPixels(ImageT const &img,
304  MaskT const &msk,
305  VarianceT const &var,
306  WeightT const &weights,
307  int const flags,
308  int const nCrude,
309  int const stride,
310  double const meanCrude,
311  double const cliplimit,
312  bool const weightsAreMultiplicative,
313  int const andMask,
314  bool const calcErrorFromInputVariance,
315  bool doGetWeighted
316  )
317  {
318  if (doGetWeighted) {
319  return processPixels<IsFinite, HasValueLtMin, HasValueGtMax, InClipRange, true>(
320  img, msk, var, weights,
321  flags, nCrude, 1, meanCrude, cliplimit, weightsAreMultiplicative, andMask,
322  calcErrorFromInputVariance);
323  } else {
324  return processPixels<IsFinite, HasValueLtMin, HasValueGtMax, InClipRange, false>(
325  img, msk, var, weights,
326  flags, nCrude, 1, meanCrude, cliplimit, weightsAreMultiplicative, andMask,
327  calcErrorFromInputVariance);
328  }
329  }
330 
331  template<typename IsFinite,
332  typename HasValueLtMin,
333  typename HasValueGtMax,
334  typename InClipRange,
335  bool useWeights,
336  typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
337  StandardReturn processPixels(ImageT const &img,
338  MaskT const &msk,
339  VarianceT const &var,
340  WeightT const &weights,
341  int const flags,
342  int const nCrude,
343  int const stride,
344  double const meanCrude,
345  double const cliplimit,
346  bool const weightsAreMultiplicative,
347  int const andMask,
348  bool const calcErrorFromInputVariance,
349  bool doCheckFinite,
350  bool doGetWeighted
351  )
352  {
353  if (doCheckFinite) {
354  return processPixels<CheckFinite, HasValueLtMin, HasValueGtMax, InClipRange, useWeights>(
355  img, msk, var, weights,
356  flags, nCrude, 1, meanCrude, cliplimit,
357  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
358  doGetWeighted);
359  } else {
360  return processPixels<AlwaysTrue, HasValueLtMin, HasValueGtMax, InClipRange, useWeights>(
361  img, msk, var, weights,
362  flags, nCrude, 1, meanCrude, cliplimit,
363  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
364  doGetWeighted);
365  }
366  }
367 
376  template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
377  StandardReturn getStandard(ImageT const &img, // image
378  MaskT const &msk, // mask
379  VarianceT const &var, // variance
380  WeightT const &weights, // weights to apply to each pixel
381  int const flags, // what to measure
382  bool const weightsAreMultiplicative, // weights are multiplicative (not inverse)
383  int const andMask, // mask of bad pixels
384  bool const calcErrorFromInputVariance, // estimate errors from variance
385  bool doCheckFinite, // check for NaN/Inf
386  bool doGetWeighted // use the weights
387  )
388  {
389  // =====================================================
390  // a crude estimate of the mean, used for numerical stability of variance
391  int nCrude = 0;
392  double meanCrude = 0.0;
393 
394  // for small numbers of values, use a small stride
395  int const nPix = img.getWidth()*img.getHeight();
396  int strideCrude;
397  if (nPix < 100) {
398  strideCrude = 2;
399  } else {
400  strideCrude = 10;
401  }
402 
403  double cliplimit = -1; // unused
404  StandardReturn values = processPixels<ChkFin, AlwaysF, AlwaysF, AlwaysT, true>(
405  img, msk, var, weights,
406  flags, nCrude, strideCrude, meanCrude,
407  cliplimit,
408  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
409  doCheckFinite, doGetWeighted);
410  nCrude = values.get<0>();
411  double sumCrude = values.get<1>();
412 
413  meanCrude = 0.0;
414  if (nCrude > 0) {
415  meanCrude = sumCrude/nCrude;
416  }
417 
418  // =======================================================
419  // Estimate the full precision variance using that crude mean
420  // - get the min and max as well
421 
422  if (flags & (afwMath::MIN | afwMath::MAX)) {
423  return processPixels<ChkFin, ChkMin, ChkMax, AlwaysT, true>(
424  img, msk, var, weights,
425  flags, nCrude, 1, meanCrude,
426  cliplimit,
427  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
428  true, doGetWeighted);
429  } else {
430  return processPixels<ChkFin, AlwaysF, AlwaysF, AlwaysT,true>(
431  img, msk, var, weights,
432  flags, nCrude, 1, meanCrude,
433  cliplimit,
434  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
435  doCheckFinite, doGetWeighted);
436  }
437  }
438 
444  template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
445  StandardReturn getStandard(ImageT const &img, // image
446  MaskT const &msk, // mask
447  VarianceT const &var, // variance
448  WeightT const &weights, // weights to apply to each pixel
449  int const flags, // what to measure
450  std::pair<double, double> const clipinfo, // the center and cliplimit for the
451  // first clip iteration
452  bool const weightsAreMultiplicative, // weights are multiplicative (not inverse)
453  int const andMask, // mask of bad pixels
454  bool const calcErrorFromInputVariance, // estimate errors from variance
455  bool doCheckFinite, // check for NaN/Inf
456  bool doGetWeighted // use the weights
457  )
458  {
459  double const center = clipinfo.first;
460  double const cliplimit = clipinfo.second;
461 
462  if (lsst::utils::isnan(center) || lsst::utils::isnan(cliplimit)) {
463  return StandardReturn(0, NaN,
464  afwMath::Statistics::Value(NaN, NaN),
465  afwMath::Statistics::Value(NaN, NaN), NaN, NaN, ~0x0);
466  }
467 
468  // =======================================================
469  // Estimate the full precision variance using that crude mean
470  int const stride = 1;
471  int nCrude = 0;
472 
473  if (flags & (afwMath::MIN | afwMath::MAX)) {
474  return processPixels<ChkFin, ChkMin, ChkMax, ChkClip, true>(
475  img, msk, var, weights,
476  flags, nCrude, stride,
477  center, cliplimit,
478  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
479  true, doGetWeighted);
480  } else { // fast loop ... just the mean & variance
481  return processPixels<ChkFin, AlwaysF, AlwaysF, ChkClip, true>(
482  img, msk, var, weights,
483  flags, nCrude, stride,
484  center, cliplimit,
485  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
486  doCheckFinite, doGetWeighted);
487  }
488  }
489 
498  template<typename Pixel>
499  double percentile(std::vector<Pixel> &img,
500  double const fraction)
501  {
502  assert (fraction >= 0.0 && fraction <= 1.0);
503 
504  int const n = img.size();
505 
506  if (n > 1) {
507 
508  double const idx = fraction*(n - 1);
509 
510  // interpolate linearly between the adjacent values
511  // For efficiency:
512  // - if we're asked for a fraction > 0.5,
513  // we'll do the second partial sort on shorter (upper) portion
514  // - otherwise, the shorter portion will be the lower one, we'll partial-sort that.
515 
516  int const q1 = static_cast<int>(idx);
517  int const q2 = q1 + 1;
518 
519  typename std::vector<Pixel>::iterator mid1 = img.begin() + q1;
520  typename std::vector<Pixel>::iterator mid2 = img.begin() + q2;
521  if (fraction > 0.5) {
522  std::nth_element(img.begin(), mid1, img.end());
523  std::nth_element(mid1, mid2, img.end());
524  } else {
525  std::nth_element(img.begin(), mid2, img.end());
526  std::nth_element(img.begin(), mid1, mid2);
527  }
528 
529  double val1 = static_cast<double>(*mid1);
530  double val2 = static_cast<double>(*mid2);
531  double w1 = (static_cast<double>(q2) - idx);
532  double w2 = (idx - static_cast<double>(q1));
533  return w1*val1 + w2*val2;
534 
535  } else if (n == 1) {
536  return img[0];
537  } else {
538  return NaN;
539  }
540  }
541 
542 
551  typedef boost::tuple<double, double, double> MedianQuartileReturn;
552 
553  template<typename Pixel>
554  MedianQuartileReturn medianAndQuartiles(std::vector<Pixel> &img)
555  {
556  int const n = img.size();
557 
558  if (n > 1) {
559 
560  double const idx50 = 0.50*(n - 1);
561  double const idx25 = 0.25*(n - 1);
562  double const idx75 = 0.75*(n - 1);
563 
564  // For efficiency:
565  // - partition at 50th, then partition the two half further to get 25th and 75th
566  // - to get the adjacent points (for interpolation), partition between 25/50, 50/75, 75/end
567  // these should be much smaller partitions
568 
569  int const q50a = static_cast<int>(idx50);
570  int const q50b = q50a + 1;
571  int const q25a = static_cast<int>(idx25);
572  int const q25b = q25a + 1;
573  int const q75a = static_cast<int>(idx75);
574  int const q75b = q75a + 1;
575 
576  typename std::vector<Pixel>::iterator mid50a = img.begin() + q50a;
577  typename std::vector<Pixel>::iterator mid50b = img.begin() + q50b;
578  typename std::vector<Pixel>::iterator mid25a = img.begin() + q25a;
579  typename std::vector<Pixel>::iterator mid25b = img.begin() + q25b;
580  typename std::vector<Pixel>::iterator mid75a = img.begin() + q75a;
581  typename std::vector<Pixel>::iterator mid75b = img.begin() + q75b;
582 
583  // get the 50th percentile, then get the 25th and 75th on the smaller partitions
584  std::nth_element(img.begin(), mid50a, img.end());
585  std::nth_element(mid50a, mid75a, img.end());
586  std::nth_element(img.begin(), mid25a, mid50a);
587 
588  // and the adjacent points for each ... use the smallest segments available.
589  std::nth_element(mid50a, mid50b, mid75a);
590  std::nth_element(mid25a, mid25b, mid50a);
591  std::nth_element(mid75a, mid75b, img.end());
592 
593  // interpolate linearly between the adjacent values
594  double val50a = static_cast<double>(*mid50a);
595  double val50b = static_cast<double>(*mid50b);
596  double w50a = (static_cast<double>(q50b) - idx50);
597  double w50b = (idx50 - static_cast<double>(q50a));
598  double median = w50a*val50a + w50b*val50b;
599 
600  double val25a = static_cast<double>(*mid25a);
601  double val25b = static_cast<double>(*mid25b);
602  double w25a = (static_cast<double>(q25b) - idx25);
603  double w25b = (idx25 - static_cast<double>(q25a));
604  double q1 = w25a*val25a + w25b*val25b;
605 
606  double val75a = static_cast<double>(*mid75a);
607  double val75b = static_cast<double>(*mid75b);
608  double w75a = (static_cast<double>(q75b) - idx75);
609  double w75b = (idx75 - static_cast<double>(q75a));
610  double q3 = w75a*val75a + w75b*val75b;
611 
612  return MedianQuartileReturn(median, q1, q3);
613  } else if (n == 1) {
614  return MedianQuartileReturn(img[0], img[0], img[0]);
615  } else {
616  return MedianQuartileReturn(NaN, NaN, NaN);
617  }
618  }
619 
620  /*********************************************************************************************************/
628  template<typename IsFinite, typename ImageT, typename MaskT, typename VarianceT>
629  boost::shared_ptr<std::vector<typename ImageT::Pixel> > makeVectorCopy(ImageT const &img,
630  MaskT const &msk,
631  VarianceT const &,
632  int const andMask
633  )
634  {
635  // Note need to keep track of allPixelOrMask here ... processPixels() does that
636  // and it always gets called
637  boost::shared_ptr<std::vector<typename ImageT::Pixel> >
638  imgcp(new std::vector<typename ImageT::Pixel>(0));
639 
640  for (int i_y = 0; i_y < img.getHeight(); ++i_y) {
641  typename MaskT::x_iterator mptr = msk.row_begin(i_y);
642  for (typename ImageT::x_iterator ptr = img.row_begin(i_y), end = img.row_end(i_y);
643  ptr != end; ++ptr) {
644  if (IsFinite()(*ptr) && !(*mptr & andMask)) {
645  imgcp->push_back(*ptr);
646  }
647  ++mptr;
648  }
649  }
650 
651  return imgcp;
652  }
653 }
654 
659  static std::map<std::string, Property> statisticsProperty;
660  if (statisticsProperty.size() == 0) {
661  statisticsProperty["NOTHING"] = NOTHING;
662  statisticsProperty["ERRORS"] = ERRORS;
663  statisticsProperty["NPOINT"] = NPOINT;
664  statisticsProperty["MEAN"] = MEAN;
665  statisticsProperty["STDEV"] = STDEV;
666  statisticsProperty["VARIANCE"] = VARIANCE;
667  statisticsProperty["MEDIAN"] = MEDIAN;
668  statisticsProperty["IQRANGE"] = IQRANGE;
669  statisticsProperty["MEANCLIP"] = MEANCLIP;
670  statisticsProperty["STDEVCLIP"] = STDEVCLIP;
671  statisticsProperty["VARIANCECLIP"] = VARIANCECLIP;
672  statisticsProperty["MIN"] = MIN;
673  statisticsProperty["MAX"] = MAX;
674  statisticsProperty["SUM"] = SUM;
675  statisticsProperty["MEANSQUARE"] = MEANSQUARE;
676  statisticsProperty["ORMASK"] = ORMASK;
677  }
678  return statisticsProperty[property];
679 }
680 
688 template<typename ImageT, typename MaskT, typename VarianceT>
690  ImageT const &img,
691  MaskT const &msk,
692  VarianceT const &var,
693  int const flags,
694  afwMath::StatisticsControl const& sctrl
695  ) :
696  _flags(flags), _mean(NaN, NaN), _variance(NaN, NaN), _min(NaN), _max(NaN), _sum(NaN),
697  _meanclip(NaN, NaN), _varianceclip(NaN, NaN), _median(NaN, NaN), _iqrange(NaN),
698  _sctrl(sctrl), _weightsAreMultiplicative(false)
699 {
700  doStatistics(img, msk, var, var, _flags, _sctrl);
701 }
702 
703 namespace {
704  template<typename T>
705  bool isEmpty(T const& t) { return t.empty(); }
706 
707  template<typename T>
708  bool isEmpty(afwImage::Image<T> const& im) { return (im.getWidth() == 0 && im.getHeight() == 0); }
709 }
710 
711 template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
713  ImageT const &img,
714  MaskT const &msk,
715  VarianceT const &var,
716  WeightT const &weights,
717  int const flags,
718  afwMath::StatisticsControl const& sctrl
719  ) :
720  _flags(flags), _mean(NaN, NaN), _variance(NaN, NaN), _min(NaN), _max(NaN), _sum(NaN),
721  _meanclip(NaN, NaN), _varianceclip(NaN, NaN), _median(NaN, NaN), _iqrange(NaN),
722  _sctrl(sctrl), _weightsAreMultiplicative(true)
723 {
724  if (!isEmpty(weights)) {
726  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
727  "You must use the weights if you provide them");
728  }
729 
730  _sctrl.setWeighted(true);
731  }
732  doStatistics(img, msk, var, weights, _flags, _sctrl);
733 }
734 
735 template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
737  ImageT const &img,
738  MaskT const &msk,
739  VarianceT const &var,
740  WeightT const &weights,
741  int const flags,
742  afwMath::StatisticsControl const& sctrl
743  )
744 {
745  _n = img.getWidth()*img.getHeight();
746  if (_n == 0) {
747  throw LSST_EXCEPT(pexExceptions::InvalidParameterError, "Image contains no pixels");
748  }
749 
750  // Check that an int's large enough to hold the number of pixels
751  assert(img.getWidth()*static_cast<double>(img.getHeight()) < std::numeric_limits<int>::max());
752 
753  // get the standard statistics
754  StandardReturn standard = getStandard(img, msk, var, weights, flags,
755  _weightsAreMultiplicative,
756  _sctrl.getAndMask(),
757  _sctrl.getCalcErrorFromInputVariance(),
758  _sctrl.getNanSafe(), _sctrl.getWeighted());
759 
760  _n = standard.get<0>();
761  _sum = standard.get<1>();
762  _mean = standard.get<2>();
763  _variance = standard.get<3>();
764  _min = standard.get<4>();
765  _max = standard.get<5>();
766  _allPixelOrMask = standard.get<6>();
767 
768  // ==========================================================
769  // now only calculate it if it's specifically requested - these all cost more!
770 
771  // copy the image for any routines that will use median or quantiles
772  if (flags & (MEDIAN | IQRANGE | MEANCLIP | STDEVCLIP | VARIANCECLIP)) {
773 
774  // make a vector copy of the image to get the median and quartiles (will move values)
775  boost::shared_ptr<std::vector<typename ImageT::Pixel> > imgcp;
776  if (_sctrl.getNanSafe()) {
777  imgcp = makeVectorCopy<ChkFin>(img, msk, var, _sctrl.getAndMask());
778  } else {
779  imgcp = makeVectorCopy<AlwaysT>(img, msk, var, _sctrl.getAndMask());
780  }
781 
782  // if we *only* want the median, just use percentile(), otherwise use medianAndQuartiles()
783  if ( (flags & (MEDIAN)) && !(flags & (IQRANGE | MEANCLIP | STDEVCLIP | VARIANCECLIP)) ) {
784  _median = Value(percentile(*imgcp, 0.5), NaN);
785  } else {
786  MedianQuartileReturn mq = medianAndQuartiles(*imgcp);
787  _median = Value(mq.get<0>(), NaN);
788  _iqrange = mq.get<2>() - mq.get<1>();
789  }
790 
791 
792  if (flags & (MEANCLIP | STDEVCLIP | VARIANCECLIP)) {
793  for (int i_i = 0; i_i < _sctrl.getNumIter(); ++i_i) {
794  double const center = ((i_i > 0) ? _meanclip : _median).first;
795  double const hwidth = (i_i > 0 && _n > 1) ?
796  _sctrl.getNumSigmaClip()*std::sqrt(_varianceclip.first) :
797  _sctrl.getNumSigmaClip()*IQ_TO_STDEV*_iqrange;
798  std::pair<double, double> const clipinfo(center, hwidth);
799 
800  StandardReturn clipped = getStandard(img, msk, var, weights, flags, clipinfo,
801  _weightsAreMultiplicative,
802  _sctrl.getAndMask(),
803  _sctrl.getCalcErrorFromInputVariance(),
804  _sctrl.getNanSafe(), _sctrl.getWeighted());
805 
806  int const nClip = clipped.get<0>();
807  _meanclip = clipped.get<2>(); // clipped mean
808  double const varClip = clipped.get<3>().first; // clipped variance
809 
810  _varianceclip = Value(varClip, varianceError(varClip, nClip));
811  // ... ignore other values
812  }
813  }
814  }
815 }
816 
817 /************************************************************************************************************/
828 std::pair<double, double> afwMath::Statistics::getResult(
829  afwMath::Property const iProp
830  ) const {
834 
835  // if iProp == NOTHING try to return their heart's delight, as specified in the constructor
836  afwMath::Property const prop =
837  static_cast<afwMath::Property>(((iProp == NOTHING) ? _flags : iProp) & ~ERRORS);
838 
839  if (!(prop & _flags)) { // we didn't calculate it
840  throw LSST_EXCEPT(pexExceptions::InvalidParameterError,
841  (boost::format("You didn't ask me to calculate %d") % prop).str());
842  }
843 
844 
845  Value ret(NaN, NaN);
846  switch (prop) {
847 
848  case NPOINT:
849  ret.first = static_cast<double>(_n);
850  if (_flags & ERRORS) {
851  ret.second = 0;
852  }
853  break;
854 
855  case SUM:
856  ret.first = static_cast<double>(_sum);
857  if (_flags & ERRORS) {
858  ret.second = 0;
859  }
860  break;
861 
862  // == means ==
863  case MEAN:
864  ret.first = _mean.first;
865  if (_flags & ERRORS) {
866  ret.second = ::sqrt(_mean.second);
867  }
868  break;
869  case MEANCLIP:
870  ret.first = _meanclip.first;
871  if ( _flags & ERRORS ) {
872  ret.second = ::sqrt(_meanclip.second);
873  }
874  break;
875 
876  // == stdevs & variances ==
877  case VARIANCE:
878  ret.first = _variance.first;
879  if (_flags & ERRORS) {
880  ret.second = ::sqrt(_variance.second);
881  }
882  break;
883  case STDEV:
884  ret.first = sqrt(_variance.first);
885  if (_flags & ERRORS) {
886  ret.second = 0.5*::sqrt(_variance.second)/ret.first;
887  }
888  break;
889  case VARIANCECLIP:
890  ret.first = _varianceclip.first;
891  if (_flags & ERRORS) {
892  ret.second = ret.second;
893  }
894  break;
895  case STDEVCLIP:
896  ret.first = sqrt(_varianceclip.first);
897  if (_flags & ERRORS) {
898  ret.second = 0.5*::sqrt(_varianceclip.second)/ret.first;
899  }
900  break;
901 
902  case MEANSQUARE:
903  ret.first = (_n - 1)/static_cast<double>(_n)*_variance.first + ::pow(_mean.first, 2);
904  if (_flags & ERRORS) {
905  ret.second = ::sqrt(2*::pow(ret.first/_n, 2)); // assumes Gaussian
906  }
907  break;
908 
909  // == other stats ==
910  case MIN:
911  ret.first = _min;
912  if ( _flags & ERRORS ) {
913  ret.second = 0;
914  }
915  break;
916  case MAX:
917  ret.first = _max;
918  if ( _flags & ERRORS ) {
919  ret.second = 0;
920  }
921  break;
922  case MEDIAN:
923  ret.first = _median.first;
924  if ( _flags & ERRORS ) {
925  ret.second = sqrt(afwGeom::HALFPI*_variance.first/_n); // assumes Gaussian
926  }
927  break;
928  case IQRANGE:
929  ret.first = _iqrange;
930  if ( _flags & ERRORS ) {
931  ret.second = NaN; // we're not estimating this properly
932  }
933  break;
934 
935  // no-op to satisfy the compiler
936  case ERRORS:
937  break;
938  // default: redundant as 'ret' is initialized to NaN, NaN
939  default: // we must have set prop to _flags
940  assert (iProp == 0);
941  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
942  "getValue() may only be called without a parameter"
943  " if you asked for only one statistic");
944  }
945  return ret;
946 }
947 
951  afwMath::Property const prop
952  ) const {
955  return getResult(prop).first;
956 }
957 
958 
962  afwMath::Property const prop
963  ) const {
967  return getResult(prop).second;
968 }
969 
970 
971 /************************************************************************************************/
976 namespace lsst {
977 namespace afw {
978 namespace math {
979 
980 template<>
985  int const flags,
986  StatisticsControl const& sctrl
987  ) :
988  _flags(flags),
989  _mean(NaN, NaN), _variance(NaN, NaN), _min(NaN), _max(NaN),
990  _meanclip(NaN, NaN), _varianceclip(NaN, NaN), _median(NaN, NaN), _iqrange(NaN),
991  _sctrl(sctrl) {
992 
993  if ((flags & ~(NPOINT | SUM)) != 0x0) {
994  throw LSST_EXCEPT(pexExceptions::InvalidParameterError, "Statistics<Mask> only supports NPOINT and SUM");
995  }
996 
998 
999  _n = msk.getWidth()*msk.getHeight();
1000  if (_n == 0) {
1001  throw LSST_EXCEPT(pexExceptions::InvalidParameterError, "Image contains no pixels");
1002  }
1003 
1004  // Check that an int's large enough to hold the number of pixels
1005  assert(msk.getWidth()*static_cast<double>(msk.getHeight()) < std::numeric_limits<int>::max());
1006 
1007  afwImage::MaskPixel sum = 0x0;
1008  for (int y = 0; y != msk.getHeight(); ++y) {
1009  for (Mask::x_iterator ptr = msk.row_begin(y), end = msk.row_end(y); ptr != end; ++ptr) {
1010  sum |= (*ptr)[0];
1011  }
1012  }
1013  _sum = sum;
1014 }
1015 
1025  int const flags,
1026  StatisticsControl const& sctrl
1027  ) {
1028  return Statistics(msk, msk, msk, flags, sctrl);
1029 }
1030 
1031 }}}
1032 
1033 /****************************************************************************************************/
1034 /*
1035  * Explicit instantiations
1036  *
1037  * explicit Statistics(MaskedImage const& img, int const flags,
1038  * StatisticsControl const& sctrl=StatisticsControl());
1039  */
1041 //
1042 #define STAT afwMath::Statistics
1043 
1044 typedef afwImage::VariancePixel VPixel;
1045 
1046 #define INSTANTIATE_MASKEDIMAGE_STATISTICS(TYPE) \
1047  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1048  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1049  afwImage::Image<VPixel> const &var, \
1050  int const flags, StatisticsControl const& sctrl); \
1051  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1052  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1053  afwImage::Image<VPixel> const &var, \
1054  afwImage::Image<VPixel> const &weights, \
1055  int const flags, StatisticsControl const& sctrl); \
1056  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1057  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1058  afwImage::Image<VPixel> const &var, \
1059  afwMath::ImageImposter<VPixel> const &weights, \
1060  int const flags, StatisticsControl const& sctrl)
1061 
1062 #define INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_MASK(TYPE) \
1063  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1064  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1065  afwImage::Image<VPixel> const &var, \
1066  int const flags, StatisticsControl const& sctrl); \
1067  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1068  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1069  afwImage::Image<VPixel> const &var, \
1070  afwImage::Image<VPixel> const &weights, \
1071  int const flags, StatisticsControl const& sctrl)
1072 
1073 #define INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_VAR(TYPE) \
1074  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1075  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1076  afwMath::MaskImposter<VPixel> const &var, \
1077  int const flags, StatisticsControl const& sctrl); \
1078  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1079  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1080  afwMath::MaskImposter<VPixel> const &var, \
1081  afwImage::Image<VPixel> const &weights, \
1082  int const flags, StatisticsControl const& sctrl); \
1083  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1084  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1085  afwMath::MaskImposter<VPixel> const &var, \
1086  afwMath::ImageImposter<VPixel> const &weights, \
1087  int const flags, StatisticsControl const& sctrl)
1088 
1089 #define INSTANTIATE_REGULARIMAGE_STATISTICS(TYPE) \
1090  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1091  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1092  afwMath::MaskImposter<VPixel> const &var, \
1093  int const flags, StatisticsControl const& sctrl)
1094 
1095 #define INSTANTIATE_VECTOR_STATISTICS(TYPE) \
1096  template STAT::Statistics(afwMath::ImageImposter<TYPE> const &img, \
1097  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1098  afwMath::MaskImposter<VPixel> const &var, \
1099  int const flags, StatisticsControl const& sctrl); \
1100  template STAT::Statistics(afwMath::ImageImposter<TYPE> const &img, \
1101  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1102  afwMath::MaskImposter<VPixel> const &var, \
1103  afwMath::ImageImposter<VPixel> const &weights, \
1104  int const flags, StatisticsControl const& sctrl)
1105 
1106 #define INSTANTIATE_IMAGE_STATISTICS(T) \
1107  INSTANTIATE_MASKEDIMAGE_STATISTICS(T); \
1108  INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_VAR(T); \
1109  INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_MASK(T); \
1110  INSTANTIATE_REGULARIMAGE_STATISTICS(T); \
1111  INSTANTIATE_VECTOR_STATISTICS(T)
1112 
1113 INSTANTIATE_IMAGE_STATISTICS(double);
1114 INSTANTIATE_IMAGE_STATISTICS(float);
1115 INSTANTIATE_IMAGE_STATISTICS(int);
1116 INSTANTIATE_IMAGE_STATISTICS(boost::uint16_t);
1117 INSTANTIATE_IMAGE_STATISTICS(boost::uint64_t);
1118 
int y
double getValue(Property const prop=NOTHING) const
Return the value of the desired property (if specified in the constructor)
Definition: Statistics.cc:950
boost::uint16_t MaskPixel
estimate sample mean
Definition: Statistics.h:67
std::pair< double, double > Value
The type used to report (value, error) for desired statistics.
Definition: Statistics.h:199
x_iterator row_begin(int y) const
Definition: Image.h:319
estimate sample N-sigma clipped mean (N set in StatisticsControl, default=3)
Definition: Statistics.h:72
x_iterator row_end(int y) const
Return an x_iterator to the end of the y&#39;th row.
Definition: Image.h:324
Statistics(ImageT const &img, MaskT const &msk, VarianceT const &var, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Constructor for Statistics object.
Definition: Statistics.cc:689
estimate sample standard deviation
Definition: Statistics.h:68
estimate sample variance
Definition: Statistics.h:69
find sum of pixels in the image
Definition: Statistics.h:78
Value getResult(Property const prop=NOTHING) const
Return the value and error in the specified statistic (e.g. MEAN)
Definition: Statistics.cc:828
int isnan(T t)
Definition: ieee.h:110
estimate sample N-sigma clipped stdev (N set in StatisticsControl, default=3)
Definition: Statistics.h:73
int const x0
Definition: saturated.cc:45
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, Scalar >::type sum(Scalar const &scalar)
Definition: operators.h:1250
find mean value of square of pixel values
Definition: Statistics.h:79
double min
Definition: attributes.cc:216
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
Include errors of requested quantities.
Definition: Statistics.h:65
We don&#39;t want anything.
Definition: Statistics.h:64
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:237
double max
Definition: attributes.cc:218
double mean
Definition: attributes.cc:217
Pass parameters to a Statistics objectA class to pass parameters which control how the stats are calc...
Definition: Statistics.h:92
ImageT::Pixel _sum
Definition: CR.cc:313
void doStatistics(ImageT const &img, MaskT const &msk, VarianceT const &var, WeightT const &weights, int const flags, StatisticsControl const &sctrl)
Definition: Statistics.cc:736
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:93
Support for 2-D images.
StatisticsControl _sctrl
Definition: Statistics.h:239
float VariancePixel
! default type for Masks and MaskedImage Masks
estimate sample maximum
Definition: Statistics.h:77
number of sample points
Definition: Statistics.h:66
double getError(Property const prop=NOTHING) const
Return the error in the desired property (if specified in the constructor)
Definition: Statistics.cc:961
int isfinite(T t)
Definition: ieee.h:100
estimate sample minimum
Definition: Statistics.h:76
estimate sample median
Definition: Statistics.h:70
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
void setWeighted(bool useWeights)
Definition: Statistics.h:138
double const HALFPI
Definition: Angle.h:20
int _n
estimate sample inter-quartile range
Definition: Statistics.h:71
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1023
Compute Image Statistics.
get the or-mask of all pixels used.
Definition: Statistics.h:80
Property stringToStatisticsProperty(std::string const property)
Conversion function to switch a string to a Property (see Statistics.h)
Definition: Statistics.cc:658
int getHeight() const
Return the number of rows in the image.
Definition: Image.h:239
Property
control what is calculated
Definition: Statistics.h:63
ImageT val
Definition: CR.cc:154
A class to represent a 2-dimensional array of pixels.
Definition: PSF.h:43
Include files required for standard LSST Exception handling.