LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
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 <cassert>
34 #include <cmath>
35 #include <cstdint>
36 #include <iostream>
37 #include <limits>
38 #include <memory>
39 #include <tuple>
40 
41 #include "lsst/pex/exceptions.h"
42 #include "lsst/afw/image/Image.h"
44 #include "lsst/afw/geom/Angle.h"
45 
46 using namespace std;
47 namespace afwImage = lsst::afw::image;
48 namespace afwMath = lsst::afw::math;
49 namespace afwGeom = lsst::afw::geom;
50 namespace pexExceptions = lsst::pex::exceptions;
51 
52 namespace {
53  double const NaN = std::numeric_limits<double>::quiet_NaN();
54  double const MAX_DOUBLE = std::numeric_limits<double>::max();
55  double const IQ_TO_STDEV = 0.741301109252802; // 1 sigma in units of iqrange (assume Gaussian)
56 
60  class AlwaysTrue {
61  public:
62  template<typename T>
63  bool operator()(T) const {
64  return true;
65  }
66  template<typename Ta, typename Tb>
67  bool operator()(Ta, Tb) const {
68  return true;
69  }
70  template<typename Ta, typename Tb, typename Tc>
71  bool operator()(Ta, Tb, Tc) const {
72  return true;
73  }
74  };
75 
79  class AlwaysFalse {
80  public:
81  template<typename T>
82  bool operator()(T) const {
83  return false;
84  }
85  template<typename Ta, typename Tb>
86  bool operator()(Ta, Tb) const {
87  return false;
88  }
89  template<typename Ta, typename Tb, typename Tc>
90  bool operator()(Ta, Tb, Tc) const {
91  return false;
92  }
93  };
94 
98  class CheckFinite {
99  public:
100  template<typename T>
101  bool operator()(T val) const {
102  return std::isfinite(static_cast<float>(val));
103  }
104  };
105 
109  class CheckValueLtMin {
110  public:
111  template<typename Tval, typename Tmin>
112  bool operator()(Tval val, Tmin min) const {
113  return (static_cast<Tmin>(val) < min);
114  }
115  };
116 
120  class CheckValueGtMax {
121  public:
122  template<typename Tval, typename Tmax>
123  bool operator()(Tval val, Tmax max) const {
124  return (static_cast<Tmax>(val) > max);
125  }
126  };
127 
131  class CheckClipRange {
132  public:
133  template<typename Tval, typename Tcen, typename Tmax>
134  bool operator()(Tval val, Tcen center, Tmax cliplimit) const {
135  Tmax tmp = fabs(val - center);
136  return (tmp <= cliplimit);
137  }
138  };
139 
140  // define some abbreviated typenames for the test templates
141  typedef CheckFinite ChkFin;
142  typedef CheckValueLtMin ChkMin;
143  typedef CheckValueGtMax ChkMax;
144  typedef CheckClipRange ChkClip;
145  typedef AlwaysTrue AlwaysT;
146  typedef AlwaysFalse AlwaysF;
147 
148  // Return the variance of a variance, assuming a Gaussian
149  // There is apparently an attempt to correct for bias in the factor (n - 1)/n. RHL
150  inline double varianceError(double const variance, int const n)
151  {
152  return 2*(n - 1)*variance*variance/static_cast<double>(n*n);
153  }
154 
155  /*********************************************************************************************************/
156  // return type for processPixels
157  typedef std::tuple<int, // n
158  double, // sum
160  afwMath::Statistics::Value, // variance
161  double, // min
162  double, // max
163  lsst::afw::image::MaskPixel // allPixelOrMask
164  > StandardReturn;
165 
166  /*
167  * Functions which convert the booleans into calls to the proper templated types, one type per
168  * recursion level
169  */
170  /*
171  * This function handles the inner summation loop, with tests templated
172  *
173  * The idea here is to allow different conditionals in the inner loop, but avoid repeating code.
174  * Each test is actually a functor which is handled through a template. If the
175  * user requests a test (eg check for NaNs), the function is instantiated with the appropriate functor.
176  * Otherwise, an 'AlwaysTrue' or 'AlwaysFalse' object is passed in. The compiler then compiles-out
177  * a test which is always false, or removes the conditional for a test which is always true.
178  */
179  template<typename IsFinite,
180  typename HasValueLtMin,
181  typename HasValueGtMax,
182  typename InClipRange,
183  bool useWeights,
184  typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
185  StandardReturn processPixels(ImageT const &img,
186  MaskT const &msk,
187  VarianceT const &var,
188  WeightT const &weights,
189  int const,
190  int const nCrude,
191  int const stride,
192  double const meanCrude,
193  double const cliplimit,
194  bool const weightsAreMultiplicative,
195  int const andMask,
196  bool const calcErrorFromInputVariance,
197  std::vector<double> const & maskPropagationThresholds
198  )
199  {
200  int n = 0;
201  double sumw = 0.0; // sum(weight) (N.b. weight will be 1.0 if !useWeights)
202  double sumw2 = 0.0; // sum(weight^2)
203  double sumx = 0; // sum(data*weight)
204  double sumx2 = 0; // sum(data*weight^2)
205 #if 1
206  double sumvw2 = 0.0; // sum(variance*weight^2)
207 #endif
208  double min = (nCrude) ? meanCrude : MAX_DOUBLE;
209  double max = (nCrude) ? meanCrude : -MAX_DOUBLE;
210 
211  afwImage::MaskPixel allPixelOrMask = 0x0;
212 
213  std::vector<double> rejectedWeightsByBit(maskPropagationThresholds.size(), 0.0);
214 
215  for (int iY = 0; iY < img.getHeight(); iY += stride) {
216 
217  typename MaskT::x_iterator mptr = msk.row_begin(iY);
218  typename VarianceT::x_iterator vptr = var.row_begin(iY);
219  typename WeightT::x_iterator wptr = weights.row_begin(iY);
220 
221  for (typename ImageT::x_iterator ptr = img.row_begin(iY), end = ptr + img.getWidth();
222  ptr != end; ++ptr, ++mptr, ++vptr, ++wptr) {
223 
224  if (IsFinite()(*ptr) && !(*mptr & andMask) &&
225  InClipRange()(*ptr, meanCrude, cliplimit) ) { // clip
226 
227  double const delta = (*ptr - meanCrude);
228 
229  if (useWeights) {
230  double weight = *wptr;
231  if (weightsAreMultiplicative) {
232  ;
233  } else {
234  if (*wptr <= 0) {
235  continue;
236  }
237  weight = 1/weight;
238  }
239 
240  sumw += weight;
241  sumw2 += weight*weight;
242  sumx += weight*delta;
243  sumx2 += weight*delta*delta;
244 
245  if (calcErrorFromInputVariance) {
246  double const var = *vptr;
247  sumvw2 += var*weight*weight;
248  }
249  } else {
250  sumx += delta;
251  sumx2 += delta*delta;
252 
253  if (calcErrorFromInputVariance) {
254  double const var = *vptr;
255  sumvw2 += var;
256  }
257  }
258 
259  allPixelOrMask |= *mptr;
260 
261  if (HasValueLtMin()(*ptr, min)) { min = *ptr; }
262  if (HasValueGtMax()(*ptr, max)) { max = *ptr; }
263  n++;
264  } else { // pixel has been clipped, rejected, etc.
265  for (int bit = 0, nBits=maskPropagationThresholds.size(); bit < nBits; ++bit) {
266  lsst::afw::image::MaskPixel mask = 1 << bit;
267  if (*mptr & mask) {
268  double weight = 1.0;
269  if (useWeights) {
270  weight = *wptr;
271  if (!weightsAreMultiplicative) {
272  if (*wptr <= 0) {
273  continue;
274  }
275  weight = 1.0 / weight;
276  }
277  }
278  rejectedWeightsByBit[bit] += weight;
279  }
280  }
281  }
282  }
283  }
284  if (n == 0) {
285  min = NaN;
286  max = NaN;
287  }
288 
289  // estimate of population mean and variance.
290  double mean, variance;
291  if (!useWeights) {
292  sumw = sumw2 = n;
293  }
294 
295  for (int bit = 0, nBits=maskPropagationThresholds.size(); bit < nBits; ++bit) {
296  double hypotheticalTotalWeight = sumw + rejectedWeightsByBit[bit];
297  rejectedWeightsByBit[bit] /= hypotheticalTotalWeight;
298  if (rejectedWeightsByBit[bit] > maskPropagationThresholds[bit]) {
299  allPixelOrMask |= (1 << bit);
300  }
301  }
302 
303 
304  // N.b. if sumw == 0 or sumw*sumw == sumw2 (e.g. n == 1) we'll get NaNs
305  // N.b. the estimator of the variance assumes that the sample points all have the same variance;
306  // otherwise, what is it that we're estimating?
307  mean = sumx/sumw;
308  variance = sumx2/sumw - ::pow(mean, 2); // biased estimator
309  variance *= sumw*sumw/(sumw*sumw - sumw2); // debias
310 
311  double meanVar; // (standard error of mean)^2
312  if (calcErrorFromInputVariance) {
313  meanVar = sumvw2/(sumw*sumw);
314  } else {
315  meanVar = variance*sumw2/(sumw*sumw);
316  }
317 
318  double varVar = varianceError(variance, n); // error in variance; incorrect if useWeights is true
319 
320  sumx += sumw*meanCrude;
321  mean += meanCrude;
322 
323  return StandardReturn(n, sumx,
324  afwMath::Statistics::Value(mean, meanVar),
325  afwMath::Statistics::Value(variance, varVar), min, max, allPixelOrMask);
326  }
327 
328  template<typename IsFinite,
329  typename HasValueLtMin,
330  typename HasValueGtMax,
331  typename InClipRange,
332  bool useWeights,
333  typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
334  StandardReturn processPixels(ImageT const &img,
335  MaskT const &msk,
336  VarianceT const &var,
337  WeightT const &weights,
338  int const flags,
339  int const nCrude,
340  int const stride,
341  double const meanCrude,
342  double const cliplimit,
343  bool const weightsAreMultiplicative,
344  int const andMask,
345  bool const calcErrorFromInputVariance,
346  bool doGetWeighted,
347  std::vector<double> const & maskPropagationThresholds
348  )
349  {
350  if (doGetWeighted) {
351  return processPixels<IsFinite, HasValueLtMin, HasValueGtMax, InClipRange, true>(
352  img, msk, var, weights,
353  flags, nCrude, 1, meanCrude, cliplimit, weightsAreMultiplicative, andMask,
354  calcErrorFromInputVariance, maskPropagationThresholds);
355  } else {
356  return processPixels<IsFinite, HasValueLtMin, HasValueGtMax, InClipRange, false>(
357  img, msk, var, weights,
358  flags, nCrude, 1, meanCrude, cliplimit, weightsAreMultiplicative, andMask,
359  calcErrorFromInputVariance, maskPropagationThresholds);
360  }
361  }
362 
363  template<typename IsFinite,
364  typename HasValueLtMin,
365  typename HasValueGtMax,
366  typename InClipRange,
367  bool useWeights,
368  typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
369  StandardReturn processPixels(ImageT const &img,
370  MaskT const &msk,
371  VarianceT const &var,
372  WeightT const &weights,
373  int const flags,
374  int const nCrude,
375  int const stride,
376  double const meanCrude,
377  double const cliplimit,
378  bool const weightsAreMultiplicative,
379  int const andMask,
380  bool const calcErrorFromInputVariance,
381  bool doCheckFinite,
382  bool doGetWeighted,
383  std::vector<double> const & maskPropagationThresholds
384  )
385  {
386  if (doCheckFinite) {
387  return processPixels<CheckFinite, HasValueLtMin, HasValueGtMax, InClipRange, useWeights>(
388  img, msk, var, weights,
389  flags, nCrude, 1, meanCrude, cliplimit,
390  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
391  doGetWeighted, maskPropagationThresholds);
392  } else {
393  return processPixels<AlwaysTrue, HasValueLtMin, HasValueGtMax, InClipRange, useWeights>(
394  img, msk, var, weights,
395  flags, nCrude, 1, meanCrude, cliplimit,
396  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
397  doGetWeighted, maskPropagationThresholds);
398  }
399  }
400 
409  template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
410  StandardReturn getStandard(ImageT const &img, // image
411  MaskT const &msk, // mask
412  VarianceT const &var, // variance
413  WeightT const &weights, // weights to apply to each pixel
414  int const flags, // what to measure
415  bool const weightsAreMultiplicative, // weights are multiplicative (not inverse)
416  int const andMask, // mask of bad pixels
417  bool const calcErrorFromInputVariance, // estimate errors from variance
418  bool doCheckFinite, // check for NaN/Inf
419  bool doGetWeighted, // use the weights
420  std::vector<double> const & maskPropagationThresholds
421  )
422  {
423  // =====================================================
424  // a crude estimate of the mean, used for numerical stability of variance
425  int nCrude = 0;
426  double meanCrude = 0.0;
427 
428  // for small numbers of values, use a small stride
429  int const nPix = img.getWidth()*img.getHeight();
430  int strideCrude;
431  if (nPix < 100) {
432  strideCrude = 2;
433  } else {
434  strideCrude = 10;
435  }
436 
437  double cliplimit = -1; // unused
438  StandardReturn values = processPixels<ChkFin, AlwaysF, AlwaysF, AlwaysT, true>(
439  img, msk, var, weights,
440  flags, nCrude, strideCrude, meanCrude,
441  cliplimit,
442  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
443  doCheckFinite, doGetWeighted,
444  maskPropagationThresholds);
445  nCrude = std::get<0>(values);
446  double sumCrude = std::get<1>(values);
447 
448  meanCrude = 0.0;
449  if (nCrude > 0) {
450  meanCrude = sumCrude/nCrude;
451  }
452 
453  // =======================================================
454  // Estimate the full precision variance using that crude mean
455  // - get the min and max as well
456 
457  if (flags & (afwMath::MIN | afwMath::MAX)) {
458  return processPixels<ChkFin, ChkMin, ChkMax, AlwaysT, true>(
459  img, msk, var, weights,
460  flags, nCrude, 1, meanCrude,
461  cliplimit,
462  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
463  true, doGetWeighted, maskPropagationThresholds);
464  } else {
465  return processPixels<ChkFin, AlwaysF, AlwaysF, AlwaysT,true>(
466  img, msk, var, weights,
467  flags, nCrude, 1, meanCrude,
468  cliplimit,
469  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
470  doCheckFinite, doGetWeighted, maskPropagationThresholds);
471  }
472  }
473 
479  template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
480  StandardReturn getStandard(ImageT const &img, // image
481  MaskT const &msk, // mask
482  VarianceT const &var, // variance
483  WeightT const &weights, // weights to apply to each pixel
484  int const flags, // what to measure
485  std::pair<double, double> const clipinfo, // the center and cliplimit for the
486  // first clip iteration
487  bool const weightsAreMultiplicative, // weights are multiplicative (not inverse)
488  int const andMask, // mask of bad pixels
489  bool const calcErrorFromInputVariance, // estimate errors from variance
490  bool doCheckFinite, // check for NaN/Inf
491  bool doGetWeighted, // use the weights,
492  std::vector<double> const & maskPropagationThresholds
493  )
494  {
495  double const center = clipinfo.first;
496  double const cliplimit = clipinfo.second;
497 
498  if (std::isnan(center) || std::isnan(cliplimit)) {
499  return StandardReturn(0, NaN,
500  afwMath::Statistics::Value(NaN, NaN),
501  afwMath::Statistics::Value(NaN, NaN), NaN, NaN, ~0x0);
502  }
503 
504  // =======================================================
505  // Estimate the full precision variance using that crude mean
506  int const stride = 1;
507  int nCrude = 0;
508 
509  if (flags & (afwMath::MIN | afwMath::MAX)) {
510  return processPixels<ChkFin, ChkMin, ChkMax, ChkClip, true>(
511  img, msk, var, weights,
512  flags, nCrude, stride,
513  center, cliplimit,
514  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
515  true, doGetWeighted, maskPropagationThresholds);
516  } else { // fast loop ... just the mean & variance
517  return processPixels<ChkFin, AlwaysF, AlwaysF, ChkClip, true>(
518  img, msk, var, weights,
519  flags, nCrude, stride,
520  center, cliplimit,
521  weightsAreMultiplicative, andMask, calcErrorFromInputVariance,
522  doCheckFinite, doGetWeighted, maskPropagationThresholds);
523  }
524  }
525 
534  template<typename Pixel>
535  double percentile(std::vector<Pixel> &img,
536  double const fraction)
537  {
538  assert (fraction >= 0.0 && fraction <= 1.0);
539 
540  int const n = img.size();
541 
542  if (n > 1) {
543 
544  double const idx = fraction*(n - 1);
545 
546  // interpolate linearly between the adjacent values
547  // For efficiency:
548  // - if we're asked for a fraction > 0.5,
549  // we'll do the second partial sort on shorter (upper) portion
550  // - otherwise, the shorter portion will be the lower one, we'll partial-sort that.
551 
552  int const q1 = static_cast<int>(idx);
553  int const q2 = q1 + 1;
554 
555  typename std::vector<Pixel>::iterator mid1 = img.begin() + q1;
556  typename std::vector<Pixel>::iterator mid2 = img.begin() + q2;
557  if (fraction > 0.5) {
558  std::nth_element(img.begin(), mid1, img.end());
559  std::nth_element(mid1, mid2, img.end());
560  } else {
561  std::nth_element(img.begin(), mid2, img.end());
562  std::nth_element(img.begin(), mid1, mid2);
563  }
564 
565  double val1 = static_cast<double>(*mid1);
566  double val2 = static_cast<double>(*mid2);
567  double w1 = (static_cast<double>(q2) - idx);
568  double w2 = (idx - static_cast<double>(q1));
569  return w1*val1 + w2*val2;
570 
571  } else if (n == 1) {
572  return img[0];
573  } else {
574  return NaN;
575  }
576  }
577 
578 
587  typedef std::tuple<double, double, double> MedianQuartileReturn;
588 
589  template<typename Pixel>
590  MedianQuartileReturn medianAndQuartiles(std::vector<Pixel> &img)
591  {
592  int const n = img.size();
593 
594  if (n > 1) {
595 
596  double const idx50 = 0.50*(n - 1);
597  double const idx25 = 0.25*(n - 1);
598  double const idx75 = 0.75*(n - 1);
599 
600  // For efficiency:
601  // - partition at 50th, then partition the two half further to get 25th and 75th
602  // - to get the adjacent points (for interpolation), partition between 25/50, 50/75, 75/end
603  // these should be much smaller partitions
604 
605  int const q50a = static_cast<int>(idx50);
606  int const q50b = q50a + 1;
607  int const q25a = static_cast<int>(idx25);
608  int const q25b = q25a + 1;
609  int const q75a = static_cast<int>(idx75);
610  int const q75b = q75a + 1;
611 
612  typename std::vector<Pixel>::iterator mid50a = img.begin() + q50a;
613  typename std::vector<Pixel>::iterator mid50b = img.begin() + q50b;
614  typename std::vector<Pixel>::iterator mid25a = img.begin() + q25a;
615  typename std::vector<Pixel>::iterator mid25b = img.begin() + q25b;
616  typename std::vector<Pixel>::iterator mid75a = img.begin() + q75a;
617  typename std::vector<Pixel>::iterator mid75b = img.begin() + q75b;
618 
619  // get the 50th percentile, then get the 25th and 75th on the smaller partitions
620  std::nth_element(img.begin(), mid50a, img.end());
621  std::nth_element(mid50a, mid75a, img.end());
622  std::nth_element(img.begin(), mid25a, mid50a);
623 
624  // and the adjacent points for each ... use the smallest segments available.
625  std::nth_element(mid50a, mid50b, mid75a);
626  std::nth_element(mid25a, mid25b, mid50a);
627  std::nth_element(mid75a, mid75b, img.end());
628 
629  // interpolate linearly between the adjacent values
630  double val50a = static_cast<double>(*mid50a);
631  double val50b = static_cast<double>(*mid50b);
632  double w50a = (static_cast<double>(q50b) - idx50);
633  double w50b = (idx50 - static_cast<double>(q50a));
634  double median = w50a*val50a + w50b*val50b;
635 
636  double val25a = static_cast<double>(*mid25a);
637  double val25b = static_cast<double>(*mid25b);
638  double w25a = (static_cast<double>(q25b) - idx25);
639  double w25b = (idx25 - static_cast<double>(q25a));
640  double q1 = w25a*val25a + w25b*val25b;
641 
642  double val75a = static_cast<double>(*mid75a);
643  double val75b = static_cast<double>(*mid75b);
644  double w75a = (static_cast<double>(q75b) - idx75);
645  double w75b = (idx75 - static_cast<double>(q75a));
646  double q3 = w75a*val75a + w75b*val75b;
647 
648  return MedianQuartileReturn(median, q1, q3);
649  } else if (n == 1) {
650  return MedianQuartileReturn(img[0], img[0], img[0]);
651  } else {
652  return MedianQuartileReturn(NaN, NaN, NaN);
653  }
654  }
655 
656  /*********************************************************************************************************/
664  template<typename IsFinite, typename ImageT, typename MaskT, typename VarianceT>
665  std::shared_ptr<std::vector<typename ImageT::Pixel> > makeVectorCopy(ImageT const &img,
666  MaskT const &msk,
667  VarianceT const &,
668  int const andMask
669  )
670  {
671  // Note need to keep track of allPixelOrMask here ... processPixels() does that
672  // and it always gets called
673  std::shared_ptr<std::vector<typename ImageT::Pixel> >
674  imgcp(new std::vector<typename ImageT::Pixel>(0));
675 
676  for (int i_y = 0; i_y < img.getHeight(); ++i_y) {
677  typename MaskT::x_iterator mptr = msk.row_begin(i_y);
678  for (typename ImageT::x_iterator ptr = img.row_begin(i_y), end = img.row_end(i_y);
679  ptr != end; ++ptr) {
680  if (IsFinite()(*ptr) && !(*mptr & andMask)) {
681  imgcp->push_back(*ptr);
682  }
683  ++mptr;
684  }
685  }
686 
687  return imgcp;
688  }
689 }
690 
691 
692 
694  int oldSize = _maskPropagationThresholds.size();
695  if (oldSize < bit) {
696  return 1.0;
697  }
698  return _maskPropagationThresholds[bit];
699 }
700 
702  int oldSize = _maskPropagationThresholds.size();
703  if (oldSize <= bit) {
704  int newSize = bit + 1;
705  _maskPropagationThresholds.resize(newSize);
706  for (int i = oldSize; i < bit; ++i) {
707  _maskPropagationThresholds[i] = 1.0;
708  }
709  }
710  _maskPropagationThresholds[bit] = threshold;
711 }
712 
713 
718  static std::map<std::string, Property> statisticsProperty;
719  if (statisticsProperty.size() == 0) {
720  statisticsProperty["NOTHING"] = NOTHING;
721  statisticsProperty["ERRORS"] = ERRORS;
722  statisticsProperty["NPOINT"] = NPOINT;
723  statisticsProperty["MEAN"] = MEAN;
724  statisticsProperty["STDEV"] = STDEV;
725  statisticsProperty["VARIANCE"] = VARIANCE;
726  statisticsProperty["MEDIAN"] = MEDIAN;
727  statisticsProperty["IQRANGE"] = IQRANGE;
728  statisticsProperty["MEANCLIP"] = MEANCLIP;
729  statisticsProperty["STDEVCLIP"] = STDEVCLIP;
730  statisticsProperty["VARIANCECLIP"] = VARIANCECLIP;
731  statisticsProperty["MIN"] = MIN;
732  statisticsProperty["MAX"] = MAX;
733  statisticsProperty["SUM"] = SUM;
734  statisticsProperty["MEANSQUARE"] = MEANSQUARE;
735  statisticsProperty["ORMASK"] = ORMASK;
736  }
737  return statisticsProperty[property];
738 }
739 
747 template<typename ImageT, typename MaskT, typename VarianceT>
749  ImageT const &img,
750  MaskT const &msk,
751  VarianceT const &var,
752  int const flags,
753  afwMath::StatisticsControl const& sctrl
754  ) :
755  _flags(flags), _mean(NaN, NaN), _variance(NaN, NaN), _min(NaN), _max(NaN), _sum(NaN),
756  _meanclip(NaN, NaN), _varianceclip(NaN, NaN), _median(NaN, NaN), _iqrange(NaN),
757  _sctrl(sctrl), _weightsAreMultiplicative(false)
758 {
759  doStatistics(img, msk, var, var, _flags, _sctrl);
760 }
761 
762 namespace {
763  template<typename T>
764  bool isEmpty(T const& t) { return t.empty(); }
765 
766  template<typename T>
767  bool isEmpty(afwImage::Image<T> const& im) { return (im.getWidth() == 0 && im.getHeight() == 0); }
768 
769  // Asserts that image dimensions are equal
770  template<typename ImageT1, typename ImageT2>
771  void checkDimensions(ImageT1 const& image1, ImageT2 const& image2)
772  {
773  if (image1.getDimensions() != image2.getDimensions()) {
774  throw LSST_EXCEPT(pexExceptions::InvalidParameterError,
775  (boost::format("Image sizes don't match: %s vs %s") %
776  image1.getDimensions() % image2.getDimensions()).str());
777  }
778  }
779 
780  // Overloads for MaskImposter (which doesn't have a size)
781  template<typename ImageT, typename PixelT>
782  void checkDimensions(ImageT const& image1, afwMath::MaskImposter<PixelT> const& image2) {}
783  template<typename ImageT, typename PixelT>
784  void checkDimensions(afwMath::MaskImposter<PixelT> const& image1, ImageT const& image2) {}
785 
786 }
787 
788 template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
790  ImageT const &img,
791  MaskT const &msk,
792  VarianceT const &var,
793  WeightT const &weights,
794  int const flags,
795  afwMath::StatisticsControl const& sctrl
796  ) :
797  _flags(flags), _mean(NaN, NaN), _variance(NaN, NaN), _min(NaN), _max(NaN), _sum(NaN),
798  _meanclip(NaN, NaN), _varianceclip(NaN, NaN), _median(NaN, NaN), _iqrange(NaN),
799  _sctrl(sctrl), _weightsAreMultiplicative(true)
800 {
801  if (!isEmpty(weights)) {
803  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
804  "You must use the weights if you provide them");
805  }
806 
807  _sctrl.setWeighted(true);
808  }
809  doStatistics(img, msk, var, weights, _flags, _sctrl);
810 }
811 
812 template<typename ImageT, typename MaskT, typename VarianceT, typename WeightT>
814  ImageT const &img,
815  MaskT const &msk,
816  VarianceT const &var,
817  WeightT const &weights,
818  int const flags,
819  afwMath::StatisticsControl const& sctrl
820  )
821 {
822  _n = img.getWidth()*img.getHeight();
823  if (_n == 0) {
824  throw LSST_EXCEPT(pexExceptions::InvalidParameterError, "Image contains no pixels");
825  }
826  checkDimensions(img, msk);
827  checkDimensions(img, var);
828  if (sctrl.getWeighted()) {
829  checkDimensions(img, weights);
830  }
831 
832  // Check that an int's large enough to hold the number of pixels
833  assert(img.getWidth()*static_cast<double>(img.getHeight()) < std::numeric_limits<int>::max());
834 
835  // get the standard statistics
836  StandardReturn standard = getStandard(img, msk, var, weights, flags,
837  _weightsAreMultiplicative,
838  _sctrl.getAndMask(),
839  _sctrl.getCalcErrorFromInputVariance(),
840  _sctrl.getNanSafe(), _sctrl.getWeighted(),
841  _sctrl._maskPropagationThresholds);
842 
843  _n = std::get<0>(standard);
844  _sum = std::get<1>(standard);
845  _mean = std::get<2>(standard);
846  _variance = std::get<3>(standard);
847  _min = std::get<4>(standard);
848  _max = std::get<5>(standard);
849  _allPixelOrMask = std::get<6>(standard);
850 
851  // ==========================================================
852  // now only calculate it if it's specifically requested - these all cost more!
853 
854  // copy the image for any routines that will use median or quantiles
855  if (flags & (MEDIAN | IQRANGE | MEANCLIP | STDEVCLIP | VARIANCECLIP)) {
856 
857  // make a vector copy of the image to get the median and quartiles (will move values)
858  std::shared_ptr<std::vector<typename ImageT::Pixel> > imgcp;
859  if (_sctrl.getNanSafe()) {
860  imgcp = makeVectorCopy<ChkFin>(img, msk, var, _sctrl.getAndMask());
861  } else {
862  imgcp = makeVectorCopy<AlwaysT>(img, msk, var, _sctrl.getAndMask());
863  }
864 
865  // if we *only* want the median, just use percentile(), otherwise use medianAndQuartiles()
866  if ( (flags & (MEDIAN)) && !(flags & (IQRANGE | MEANCLIP | STDEVCLIP | VARIANCECLIP)) ) {
867  _median = Value(percentile(*imgcp, 0.5), NaN);
868  } else {
869  MedianQuartileReturn mq = medianAndQuartiles(*imgcp);
870  _median = Value(std::get<0>(mq), NaN);
871  _iqrange = std::get<2>(mq) - std::get<1>(mq);
872  }
873 
874 
875  if (flags & (MEANCLIP | STDEVCLIP | VARIANCECLIP)) {
876  for (int i_i = 0; i_i < _sctrl.getNumIter(); ++i_i) {
877  double const center = ((i_i > 0) ? _meanclip : _median).first;
878  double const hwidth = (i_i > 0 && _n > 1) ?
879  _sctrl.getNumSigmaClip()*std::sqrt(_varianceclip.first) :
880  _sctrl.getNumSigmaClip()*IQ_TO_STDEV*_iqrange;
881  std::pair<double, double> const clipinfo(center, hwidth);
882 
883  StandardReturn clipped = getStandard(img, msk, var, weights, flags, clipinfo,
884  _weightsAreMultiplicative,
885  _sctrl.getAndMask(),
886  _sctrl.getCalcErrorFromInputVariance(),
887  _sctrl.getNanSafe(), _sctrl.getWeighted(),
888  _sctrl._maskPropagationThresholds);
889 
890  int const nClip = std::get<0>(clipped);
891  _meanclip = std::get<2>(clipped); // clipped mean
892  double const varClip = std::get<3>(clipped).first; // clipped variance
893 
894  _varianceclip = Value(varClip, varianceError(varClip, nClip));
895  // ... ignore other values
896  }
897  }
898  }
899 }
900 
901 /************************************************************************************************************/
912 std::pair<double, double> afwMath::Statistics::getResult(
913  afwMath::Property const iProp
914  ) const {
918 
919  // if iProp == NOTHING try to return their heart's delight, as specified in the constructor
920  afwMath::Property const prop =
921  static_cast<afwMath::Property>(((iProp == NOTHING) ? _flags : iProp) & ~ERRORS);
922 
923  if (!(prop & _flags)) { // we didn't calculate it
924  throw LSST_EXCEPT(pexExceptions::InvalidParameterError,
925  (boost::format("You didn't ask me to calculate %d") % prop).str());
926  }
927 
928 
929  Value ret(NaN, NaN);
930  switch (prop) {
931 
932  case NPOINT:
933  ret.first = static_cast<double>(_n);
934  if (_flags & ERRORS) {
935  ret.second = 0;
936  }
937  break;
938 
939  case SUM:
940  ret.first = static_cast<double>(_sum);
941  if (_flags & ERRORS) {
942  ret.second = 0;
943  }
944  break;
945 
946  // == means ==
947  case MEAN:
948  ret.first = _mean.first;
949  if (_flags & ERRORS) {
950  ret.second = ::sqrt(_mean.second);
951  }
952  break;
953  case MEANCLIP:
954  ret.first = _meanclip.first;
955  if ( _flags & ERRORS ) {
956  ret.second = ::sqrt(_meanclip.second);
957  }
958  break;
959 
960  // == stdevs & variances ==
961  case VARIANCE:
962  ret.first = _variance.first;
963  if (_flags & ERRORS) {
964  ret.second = ::sqrt(_variance.second);
965  }
966  break;
967  case STDEV:
968  ret.first = sqrt(_variance.first);
969  if (_flags & ERRORS) {
970  ret.second = 0.5*::sqrt(_variance.second)/ret.first;
971  }
972  break;
973  case VARIANCECLIP:
974  ret.first = _varianceclip.first;
975  if (_flags & ERRORS) {
976  ret.second = ret.second;
977  }
978  break;
979  case STDEVCLIP:
980  ret.first = sqrt(_varianceclip.first);
981  if (_flags & ERRORS) {
982  ret.second = 0.5*::sqrt(_varianceclip.second)/ret.first;
983  }
984  break;
985 
986  case MEANSQUARE:
987  ret.first = (_n - 1)/static_cast<double>(_n)*_variance.first + ::pow(_mean.first, 2);
988  if (_flags & ERRORS) {
989  ret.second = ::sqrt(2*::pow(ret.first/_n, 2)); // assumes Gaussian
990  }
991  break;
992 
993  // == other stats ==
994  case MIN:
995  ret.first = _min;
996  if ( _flags & ERRORS ) {
997  ret.second = 0;
998  }
999  break;
1000  case MAX:
1001  ret.first = _max;
1002  if ( _flags & ERRORS ) {
1003  ret.second = 0;
1004  }
1005  break;
1006  case MEDIAN:
1007  ret.first = _median.first;
1008  if ( _flags & ERRORS ) {
1009  ret.second = sqrt(afwGeom::HALFPI*_variance.first/_n); // assumes Gaussian
1010  }
1011  break;
1012  case IQRANGE:
1013  ret.first = _iqrange;
1014  if ( _flags & ERRORS ) {
1015  ret.second = NaN; // we're not estimating this properly
1016  }
1017  break;
1018 
1019  // no-op to satisfy the compiler
1020  case ERRORS:
1021  break;
1022  // default: redundant as 'ret' is initialized to NaN, NaN
1023  default: // we must have set prop to _flags
1024  assert (iProp == 0);
1025  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
1026  "getValue() may only be called without a parameter"
1027  " if you asked for only one statistic");
1028  }
1029  return ret;
1030 }
1031 
1035  afwMath::Property const prop
1036  ) const {
1039  return getResult(prop).first;
1040 }
1041 
1042 
1046  afwMath::Property const prop
1047  ) const {
1051  return getResult(prop).second;
1052 }
1053 
1054 
1055 /************************************************************************************************/
1060 namespace lsst {
1061 namespace afw {
1062 namespace math {
1063 
1064 template<>
1069  int const flags,
1070  StatisticsControl const& sctrl
1071  ) :
1072  _flags(flags),
1073  _mean(NaN, NaN), _variance(NaN, NaN), _min(NaN), _max(NaN),
1074  _meanclip(NaN, NaN), _varianceclip(NaN, NaN), _median(NaN, NaN), _iqrange(NaN),
1075  _sctrl(sctrl) {
1076 
1077  if ((flags & ~(NPOINT | SUM)) != 0x0) {
1078  throw LSST_EXCEPT(pexExceptions::InvalidParameterError, "Statistics<Mask> only supports NPOINT and SUM");
1079  }
1080 
1082 
1083  _n = msk.getWidth()*msk.getHeight();
1084  if (_n == 0) {
1085  throw LSST_EXCEPT(pexExceptions::InvalidParameterError, "Image contains no pixels");
1086  }
1087 
1088  // Check that an int's large enough to hold the number of pixels
1089  assert(msk.getWidth()*static_cast<double>(msk.getHeight()) < std::numeric_limits<int>::max());
1090 
1091  afwImage::MaskPixel sum = 0x0;
1092  for (int y = 0; y != msk.getHeight(); ++y) {
1093  for (Mask::x_iterator ptr = msk.row_begin(y), end = msk.row_end(y); ptr != end; ++ptr) {
1094  sum |= (*ptr)[0];
1095  }
1096  }
1097  _sum = sum;
1098 }
1099 
1109  int const flags,
1110  StatisticsControl const& sctrl
1111  ) {
1112  return Statistics(msk, msk, msk, flags, sctrl);
1113 }
1114 
1115 }}}
1116 
1117 /****************************************************************************************************/
1118 /*
1119  * Explicit instantiations
1120  *
1121  * explicit Statistics(MaskedImage const& img, int const flags,
1122  * StatisticsControl const& sctrl=StatisticsControl());
1123  */
1125 //
1126 #define STAT afwMath::Statistics
1127 
1128 typedef afwImage::VariancePixel VPixel;
1129 
1130 #define INSTANTIATE_MASKEDIMAGE_STATISTICS(TYPE) \
1131  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1132  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1133  afwImage::Image<VPixel> const &var, \
1134  int const flags, StatisticsControl const& sctrl); \
1135  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1136  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1137  afwImage::Image<VPixel> const &var, \
1138  afwImage::Image<VPixel> const &weights, \
1139  int const flags, StatisticsControl const& sctrl); \
1140  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1141  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1142  afwImage::Image<VPixel> const &var, \
1143  afwMath::ImageImposter<VPixel> const &weights, \
1144  int const flags, StatisticsControl const& sctrl)
1145 
1146 #define INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_MASK(TYPE) \
1147  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1148  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1149  afwImage::Image<VPixel> const &var, \
1150  int const flags, StatisticsControl const& sctrl); \
1151  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1152  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1153  afwImage::Image<VPixel> const &var, \
1154  afwImage::Image<VPixel> const &weights, \
1155  int const flags, StatisticsControl const& sctrl)
1156 
1157 #define INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_VAR(TYPE) \
1158  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1159  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1160  afwMath::MaskImposter<VPixel> const &var, \
1161  int const flags, StatisticsControl const& sctrl); \
1162  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1163  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1164  afwMath::MaskImposter<VPixel> const &var, \
1165  afwImage::Image<VPixel> const &weights, \
1166  int const flags, StatisticsControl const& sctrl); \
1167  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1168  afwImage::Mask<afwImage::MaskPixel> const &msk, \
1169  afwMath::MaskImposter<VPixel> const &var, \
1170  afwMath::ImageImposter<VPixel> const &weights, \
1171  int const flags, StatisticsControl const& sctrl)
1172 
1173 #define INSTANTIATE_REGULARIMAGE_STATISTICS(TYPE) \
1174  template STAT::Statistics(afwImage::Image<TYPE> const &img, \
1175  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1176  afwMath::MaskImposter<VPixel> const &var, \
1177  int const flags, StatisticsControl const& sctrl)
1178 
1179 #define INSTANTIATE_VECTOR_STATISTICS(TYPE) \
1180  template STAT::Statistics(afwMath::ImageImposter<TYPE> const &img, \
1181  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1182  afwMath::MaskImposter<VPixel> const &var, \
1183  int const flags, StatisticsControl const& sctrl); \
1184  template STAT::Statistics(afwMath::ImageImposter<TYPE> const &img, \
1185  afwMath::MaskImposter<afwImage::MaskPixel> const &msk, \
1186  afwMath::MaskImposter<VPixel> const &var, \
1187  afwMath::ImageImposter<VPixel> const &weights, \
1188  int const flags, StatisticsControl const& sctrl)
1189 
1190 #define INSTANTIATE_IMAGE_STATISTICS(T) \
1191  INSTANTIATE_MASKEDIMAGE_STATISTICS(T); \
1192  INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_VAR(T); \
1193  INSTANTIATE_MASKEDIMAGE_STATISTICS_NO_MASK(T); \
1194  INSTANTIATE_REGULARIMAGE_STATISTICS(T); \
1195  INSTANTIATE_VECTOR_STATISTICS(T)
1196 
1197 INSTANTIATE_IMAGE_STATISTICS(double);
1198 INSTANTIATE_IMAGE_STATISTICS(float);
1199 INSTANTIATE_IMAGE_STATISTICS(int);
1200 INSTANTIATE_IMAGE_STATISTICS(std::uint16_t);
1201 INSTANTIATE_IMAGE_STATISTICS(std::uint64_t);
1202 
int y
std::uint16_t MaskPixel
tbl::Key< double > weight
get the or-mask of all pixels used.
Definition: Statistics.h:80
estimate sample minimum
Definition: Statistics.h:76
void doStatistics(ImageT const &img, MaskT const &msk, VarianceT const &var, WeightT const &weights, int const flags, StatisticsControl const &sctrl)
Definition: Statistics.cc:813
estimate sample standard deviation
Definition: Statistics.h:68
find mean value of square of pixel values
Definition: Statistics.h:79
estimate sample maximum
Definition: Statistics.h:77
void setMaskPropagationThreshold(int bit, double threshold)
Definition: Statistics.cc:701
We don&#39;t want anything.
Definition: Statistics.h:64
unsigned long _n
Definition: RandomImage.cc:68
Include errors of requested quantities.
Definition: Statistics.h:65
int const x0
Definition: saturated.cc:45
bool val
Statistics(ImageT const &img, MaskT const &msk, VarianceT const &var, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Constructor for Statistics object.
Definition: Statistics.cc:748
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
estimate sample N-sigma clipped stdev (N set in StatisticsControl, default=3)
Definition: Statistics.h:73
double getValue(Property const prop=NOTHING) const
Return the value of the desired property (if specified in the constructor)
Definition: Statistics.cc:1034
std::pair< double, double > Value
The type used to report (value, error) for desired statistics.
Definition: Statistics.h:215
estimate sample median
Definition: Statistics.h:70
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:319
estimate sample N-sigma clipped mean (N set in StatisticsControl, default=3)
Definition: Statistics.h:72
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:92
x_iterator row_end(int y) const
Return an x_iterator to the end of the y&#39;th row.
Definition: Image.h:326
A Mask wrapper to provide an infinite_iterator for Mask::row_begin(). This allows a fake Mask to be p...
Definition: Statistics.h:289
number of sample points
Definition: Statistics.h:66
Support for 2-D images.
int getHeight() const
Return the number of rows in the image.
Definition: Image.h:241
void setWeighted(bool useWeights)
Definition: Statistics.h:149
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
Value getResult(Property const prop=NOTHING) const
Return the value and error in the specified statistic (e.g. MEAN)
Definition: Statistics.cc:912
estimate sample inter-quartile range
Definition: Statistics.h:71
double getError(Property const prop=NOTHING) const
Return the error in the desired property (if specified in the constructor)
Definition: Statistics.cc:1045
estimate sample mean
Definition: Statistics.h:67
double const HALFPI
Definition: Angle.h:19
double getMaskPropagationThreshold(int bit) const
Definition: Statistics.cc:693
estimate sample variance
Definition: Statistics.h:69
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1107
Compute Image Statistics.
Property stringToStatisticsProperty(std::string const property)
Conversion function to switch a string to a Property (see Statistics.h)
Definition: Statistics.cc:717
x_iterator row_begin(int y) const
Definition: Image.h:321
find sum of pixels in the image
Definition: Statistics.h:78
Include files required for standard LSST Exception handling.
float VariancePixel
! default type for Masks and MaskedImage Masks
Property
control what is calculated
Definition: Statistics.h:63
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:239
A class to represent a 2-dimensional array of pixels.
Definition: PSF.h:43
StatisticsControl _sctrl
Definition: Statistics.h:255