LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
_scaling.cc
Go to the documentation of this file.
1 /*
2  * Calculate some scalings. Legacy code from NAOJ, which could probably be rewritten in python
3  * if the need arose. It could certainly use more LSST primitives (e.g. line fitting)
4  */
5 #include <cstdint>
6 #include <cmath>
7 #include <algorithm>
8 #include <cstdio>
9 #include <vector>
10 #include <stdexcept>
11 
12 #include "lsst/pex/exceptions.h"
13 #include "lsst/afw/image/Image.h"
14 
15 namespace lsst {
16 namespace afw {
17 namespace display {
18 
19 template <class T>
20 static void getSample(image::Image<T> const& image, std::size_t const nSamples, std::vector<T>& vSample) {
21  int const width = image.getWidth();
22  int const height = image.getHeight();
23 
24  // extract, from image, about nSample samples
25  // such that they form a grid.
26  vSample.reserve(nSamples);
27 
28  int const initialStride =
29  std::max(1, static_cast<int>(std::sqrt(width * height / static_cast<double>(nSamples))));
30 
31  for (int stride = initialStride; stride >= 1; --stride) {
32  vSample.clear();
33 
34  for (int y = 0; y < height; y += stride) {
35  for (int x = 0; x < width; x += stride) {
36  T const elem = image(x, y);
37  if (std::isfinite(elem)) {
38  vSample.push_back(elem);
39  }
40  }
41  }
42 
43  // if more than 80% of nSamples were sampled, OK.
44  if (5 * vSample.size() > 4 * nSamples) {
45  break;
46  }
47  }
48 }
49 
50 static inline double computeSigma(std::vector<double> const& vFlat, std::vector<int> const& vBadPix,
51  std::size_t const nGoodPix) {
52  if (nGoodPix <= 1) {
53  return 0;
54  }
55 
56  double sumz = 0, sumsq = 0;
57  for (unsigned i = 0; i < vFlat.size(); ++i) {
58  if (!vBadPix[i]) {
59  double const z = vFlat[i];
60 
61  sumz += z;
62  sumsq += z * z;
63  }
64  }
65 
66  double const goodPix = nGoodPix;
67  double const tmp = sumsq / (goodPix - 1) - sumz * sumz / (goodPix * (goodPix - 1));
68 
69  return (tmp > 0) ? std::sqrt(tmp) : 0;
70 }
71 
72 template <class T>
73 static std::pair<double, double> fitLine(
74  int* nGoodPixOut, // returned; it'd be nice to use std::tuple from C++11
75  std::vector<T> const& vSample, double const nSigmaClip, int const nGrow, int const minpix,
76  int const nIter) {
77  // map the indices of vSample to [-1.0, 1.0]
78  double const xscale = 2.0 / (vSample.size() - 1);
79  std::vector<double> xnorm;
80  xnorm.reserve(vSample.size());
81  for (std::size_t i = 0; i < vSample.size(); ++i) {
82  xnorm.push_back(i * xscale - 1.0);
83  }
84 
85  // Mask that is used in k-sigma clipping
86  std::vector<int> vBadPix(vSample.size(), 0);
87 
88  int nGoodPix = vSample.size();
89  int nGoodPixOld = nGoodPix + 1;
90 
91  // values to be obtained
92  double intercept = 0;
93  double slope = 0;
94 
95  for (int iteration = 0; iteration < nIter; ++iteration) {
96  if (nGoodPix < minpix || nGoodPix >= nGoodPixOld) {
97  break;
98  }
99 
100  double sum = nGoodPix;
101  double sumx = 0, sumy = 0, sumxx = 0, sumxy = 0;
102  for (std::size_t i = 0; i < vSample.size(); ++i) {
103  if (!vBadPix[i]) {
104  double const x = xnorm[i];
105  double const y = vSample[i];
106 
107  sumx += x;
108  sumy += y;
109  sumxx += x * x;
110  sumxy += x * y;
111  }
112  }
113 
114  double delta = sum * sumxx - sumx * sumx;
115 
116  // slope and intercept
117  intercept = (sumxx * sumy - sumx * sumxy) / delta;
118  slope = (sum * sumxy - sumx * sumy) / delta;
119 
120  // residue
121  std::vector<double> vFlat;
122  vFlat.reserve(vSample.size());
123  for (unsigned i = 0; i < vSample.size(); ++i) {
124  vFlat.push_back(vSample[i] - (xnorm[i] * slope + intercept));
125  }
126 
127  // Threshold of k-sigma clipping
128  double const sigma = computeSigma(vFlat, vBadPix, nGoodPix);
129  double const hcut = sigma * nSigmaClip;
130  double const lcut = -hcut;
131 
132  // revise vBadPix
133  nGoodPixOld = nGoodPix;
134  nGoodPix = 0;
135 
136  for (unsigned i = 0; i < vSample.size(); ++i) {
137  double val = vFlat[i];
138  if (val < lcut || hcut < val) {
139  vBadPix[i] = 1;
140  }
141  }
142 
143  // blurr vBadPix
144  std::vector<int> vBadPix_new;
145  vBadPix_new.reserve(vSample.size());
146  for (unsigned x = 0; x < vSample.size(); ++x) {
147  int imin = (static_cast<int>(x) > nGrow) ? x - nGrow : -1;
148  int val = 0;
149  for (int i = x; i > imin; --i) {
150  val += vBadPix[i];
151  }
152  vBadPix_new.push_back(val ? 1 : 0);
153  if (!val) {
154  ++nGoodPix;
155  }
156  }
157  vBadPix = vBadPix_new;
158  }
159 
160  // return the scale of x-axis
161  *nGoodPixOut = nGoodPix;
162 
163  return std::make_pair(intercept - slope, slope * xscale);
164 }
165 
166 template <class T>
167 std::pair<double, double> getZScale(image::Image<T> const& image, int const nSamples, double const contrast) {
168  // extract samples
169  std::vector<T> vSample;
170  getSample(image, nSamples, vSample);
171  int nPix = vSample.size();
172 
173  if (vSample.empty()) {
174  throw LSST_EXCEPT(pex::exceptions::RuntimeError, "ZScale: No pixel in image is finite");
175  }
176 
177  std::sort(vSample.begin(), vSample.end());
178 
179  // max, min, median
180  // N.b. you can get a median in linear time, but we need the sorted array for fitLine()
181  // If we wanted to speed this up, the best option would be to quantize
182  // the pixel values and build a histogram
183  double const zmin = vSample.front();
184  double const zmax = vSample.back();
185  int const iCenter = nPix / 2;
186  T median = (nPix & 1) ? vSample[iCenter] : (vSample[iCenter] + vSample[iCenter + 1]) / 2;
187 
188  // fit a line to the sorted sample
189  const int maxRejectionRatio = 2;
190  const int npixelsMin = 5;
191 
192  int minpix = std::max(npixelsMin, nPix / maxRejectionRatio);
193  int nGrow = std::max(1, nPix / 100);
194 
195  const double nSigmaClip = 2.5;
196  const int nIterations = 5;
197 
198  int nGoodPix = 0;
199  std::pair<double, double> ret = fitLine(&nGoodPix, vSample, nSigmaClip, nGrow, minpix, nIterations);
200 #if 0 // unused, but calculated and potentially useful
201  double const zstart = ret.first;
202 #endif
203  double const zslope = ret.second;
204 
205  double z1, z2;
206  if (nGoodPix < minpix) {
207  z1 = zmin;
208  z2 = zmax;
209  } else {
210  double const slope = zslope / contrast;
211 
212  z1 = std::max(zmin, median - iCenter * slope);
213  z2 = std::min(zmax, median + (nPix - iCenter - 1) * slope);
214  }
215 
216  return std::make_pair(z1, z2);
217 }
218 //
219 // Explicit instantiations
220 #define INSTANTIATE_GETZSCALE(T) \
221  template std::pair<double, double> getZScale(image::Image<T> const& image, int const nSamples, \
222  double const contrast)
223 
226 } // namespace display
227 } // namespace afw
228 } // namespace lsst
#define INSTANTIATE_GETZSCALE(T)
Definition: _scaling.cc:220
double x
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
afw::table::Key< double > sigma
Definition: GaussianPsf.cc:49
afw::table::Key< afw::table::Array< ImagePixelT > > image
double z
Definition: Match.cc:44
int y
Definition: SpanSet.cc:48
T back(T... args)
T begin(T... args)
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:51
Reports errors that are due to events beyond the control of the program.
Definition: Runtime.h:104
T clear(T... args)
T empty(T... args)
T end(T... args)
T front(T... args)
T isfinite(T... args)
T make_pair(T... args)
T max(T... args)
T min(T... args)
std::pair< double, double > getZScale(image::Image< T > const &image, int const nSamples, double const contrast)
Calculate an IRAF/ds9-style zscaling.
Definition: _scaling.cc:167
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
A base class for image defects.
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)
T sqrt(T... args)
ImageT val
Definition: CR.cc:146