LSSTApplications  18.0.0+106,18.0.0+50,19.0.0,19.0.0+1,19.0.0+10,19.0.0+11,19.0.0+13,19.0.0+17,19.0.0+2,19.0.0-1-g20d9b18+6,19.0.0-1-g425ff20,19.0.0-1-g5549ca4,19.0.0-1-g580fafe+6,19.0.0-1-g6fe20d0+1,19.0.0-1-g7011481+9,19.0.0-1-g8c57eb9+6,19.0.0-1-gb5175dc+11,19.0.0-1-gdc0e4a7+9,19.0.0-1-ge272bc4+6,19.0.0-1-ge3aa853,19.0.0-10-g448f008b,19.0.0-12-g6990b2c,19.0.0-2-g0d9f9cd+11,19.0.0-2-g3d9e4fb2+11,19.0.0-2-g5037de4,19.0.0-2-gb96a1c4+3,19.0.0-2-gd955cfd+15,19.0.0-3-g2d13df8,19.0.0-3-g6f3c7dc,19.0.0-4-g725f80e+11,19.0.0-4-ga671dab3b+1,19.0.0-4-gad373c5+3,19.0.0-5-ga2acb9c+2,19.0.0-5-gfe96e6c+2,w.2020.01
LSSTDataManagementBasePackage
BaselineUtils.cc
Go to the documentation of this file.
1 #include <list>
2 #include <cmath>
3 #include <cstdint>
4 
5 #include "lsst/log/Log.h"
6 #include "lsst/geom.h"
8 #include "lsst/pex/exceptions.h"
9 #include "lsst/afw/geom/Span.h"
10 #include "lsst/afw/geom/Box.h"
11 
12 using std::lround;
13 
14 namespace image = lsst::afw::image;
15 namespace det = lsst::afw::detection;
16 namespace deblend = lsst::meas::deblender;
17 namespace afwGeom = lsst::afw::geom;
18 namespace geom = lsst::geom;
19 
20 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
22 
23 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
24 const int
26 
27 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
29 
30 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
32 
33 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
35 
36 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
38 
39 static bool span_compare(afwGeom::Span const & sp1,
40  afwGeom::Span const & sp2) {
41  return (sp1 < sp2);
42 }
43 
44 namespace {
45  void nearestFootprint(std::vector<PTR(det::Footprint)> const& foots,
48  {
49  /*
50  * insert the footprints into an image, set all the pixels to the
51  * Manhattan distance from the nearest set pixel.
52  */
53  typedef std::uint16_t dtype;
54  typedef std::uint16_t itype;
55 
56  const itype nil = 0xffff;
57 
58  *argmin = 0;
59  *dist = 0;
60 
61  {
62  int i = 0;
63  for (std::shared_ptr<det::Footprint> const & foot : foots) {
64  foot->getSpans()->setImage(*argmin, static_cast<itype>(i));
65  foot->getSpans()->setImage(*dist, static_cast<dtype>(1));
66  ++i;
67  }
68  }
69 
70  int const height = dist->getHeight();
71  int const width = dist->getWidth();
72 
73  // traverse from bottom left to top right
74  for (int y = 0; y != height; ++y) {
75  image::Image<dtype>::xy_locator dim = dist->xy_at(0, y);
76  image::Image<itype>::xy_locator aim = argmin->xy_at(0, y);
77  for (int x = 0; x != width; ++x, ++dim.x(), ++aim.x()) {
78  if (dim(0, 0) == 1) {
79  // first pass and pixel was on, it gets a zero
80  dim(0, 0) = 0;
81  // its argmin is already set
82  } else {
83  // pixel was off. It is at most the sum of lengths of
84  // the array away from a pixel that is on
85  dim(0, 0) = width + height;
86  aim(0, 0) = nil;
87  // or one more than the pixel to the north
88  if (y > 0) {
89  dtype ndist = dim(0,-1) + 1;
90  if (ndist < dim(0,0)) {
91  dim(0,0) = ndist;
92  aim(0,0) = aim(0,-1);
93  }
94  }
95  // or one more than the pixel to the west
96  if (x > 0) {
97  dtype ndist = dim(-1,0) + 1;
98  if (ndist < dim(0,0)) {
99  dim(0,0) = ndist;
100  aim(0,0) = aim(-1,0);
101  }
102  }
103  }
104  }
105  }
106  // traverse from top right to bottom left
107  for (int y = height - 1; y >= 0; --y) {
108  image::Image<dtype>::xy_locator dim = dist->xy_at(width-1, y);
109  image::Image<itype>::xy_locator aim = argmin->xy_at(width-1, y);
110  for (int x = width - 1; x >= 0; --x, --dim.x(), --aim.x()) {
111  // either what we had on the first pass or one more than
112  // the pixel to the south
113  if (y + 1 < height) {
114  dtype ndist = dim(0,1) + 1;
115  if (ndist < dim(0,0)) {
116  dim(0,0) = ndist;
117  aim(0,0) = aim(0,1);
118  }
119  }
120  // or one more than the pixel to the east
121  if (x + 1 < width) {
122  dtype ndist = dim(1,0) + 1;
123  if (ndist < dim(0,0)) {
124  dim(0,0) = ndist;
125  aim(0,0) = aim(1,0);
126  }
127  }
128  }
129  }
130  }
131 } // end anonymous namespace
132 
146 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
147 void
149 medianFilter(ImageT const& img,
150  ImageT & out,
151  int halfsize) {
152  int S = halfsize*2 + 1;
153  int SS = S*S;
154  typedef typename ImageT::xy_locator xy_loc;
155  xy_loc pix = img.xy_at(halfsize,halfsize);
157  for (int i=0; i<S; ++i) {
158  for (int j=0; j<S; ++j) {
159  locs.push_back(pix.cache_location(j-halfsize, i-halfsize));
160  }
161  }
162  int W = img.getWidth();
163  int H = img.getHeight();
164  ImagePixelT vals[S*S];
165  for (int y=halfsize; y<H-halfsize; ++y) {
166  xy_loc inpix = img.xy_at(halfsize, y), end = img.xy_at(W-halfsize, y);
167  for (typename ImageT::x_iterator optr = out.row_begin(y) + halfsize;
168  inpix != end; ++inpix.x(), ++optr) {
169  for (int i=0; i<SS; ++i)
170  vals[i] = inpix[locs[i]];
171  std::nth_element(vals, vals+SS/2, vals+SS);
172  *optr = vals[SS/2];
173  }
174  }
175 
176  // grumble grumble margins
177  for (int y=0; y<2*halfsize; ++y) {
178  int iy = y;
179  if (y >= halfsize)
180  iy = H - 1 - (y-halfsize);
181  typename ImageT::x_iterator optr = out.row_begin(iy);
182  typename ImageT::x_iterator iptr = img.row_begin(iy), end=img.row_end(iy);
183  for (; iptr != end; ++iptr,++optr)
184  *optr = *iptr;
185  }
186  for (int y=halfsize; y<H-halfsize; ++y) {
187  typename ImageT::x_iterator optr = out.row_begin(y);
188  typename ImageT::x_iterator iptr = img.row_begin(y), end=img.row_begin(y)+halfsize;
189  for (; iptr != end; ++iptr,++optr)
190  *optr = *iptr;
191  iptr = img.row_begin(y) + ((W-1) - halfsize);
192  end = img.row_begin(y) + (W-1);
193  optr = out.row_begin(y) + ((W-1) - halfsize);
194  for (; iptr != end; ++iptr,++optr)
195  *optr = *iptr;
196  }
197 
198 }
199 
224 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
225 void
228  ImageT & img,
229  det::PeakRecord const& peak) {
230 
231  int cx = peak.getIx();
232  int cy = peak.getIy();
233  int ix0 = img.getX0();
234  int iy0 = img.getY0();
235  int iW = img.getWidth();
236  int iH = img.getHeight();
237 
238  ImagePtrT shadowingImg = ImagePtrT(new ImageT(img, true));
239 
240  int DW = std::max(cx - img.getX0(), img.getX0() + img.getWidth() - cx);
241  int DH = std::max(cy - img.getY0(), img.getY0() + img.getHeight() - cy);
242 
243  const int S = 5;
244 
245  // Work out from the peak in chunks of "S" pixels.
246  int s;
247  for (s = 0; s < std::max(DW,DH); s += S) {
248  int p;
249  for (p=0; p<S; p++) {
250  // visit pixels with L_inf distance = s + p from the
251  // center (ie, the s+p'th square ring of pixels)
252  // L is the half-length of the ring (box).
253  int L = s+p;
254  int x = L, y = -L;
255  int dx = 0, dy = 0; // initialized here to satisfy the
256  // compiler; initialized for real
257  // below (first time through loop)
258  /*
259  int i;
260  int leg;
261  for (i=0; i<(8*L); i++, x += dx, y += dy) {
262  if (i % (2*L) == 0) {
263  leg = (i/(2*L));
264  dx = ( leg % 2) * (-1 + 2*(leg/2));
265  dy = ((leg+1) % 2) * ( 1 - 2*(leg/2));
266  }
267  */
268 
269  /*
270  We visit pixels in a box of "radius" L, in this order:
271 
272  L=1:
273 
274  4 3 2
275  5 1
276  6 7 0
277 
278  L=2:
279 
280  8 7 6 5 4
281  9 3
282  10 2
283  11 1
284  12 13 14 15 0
285 
286  Note that the number of pixel visited is 8*L, and that we
287  change "dx" or "dy" each "2*L" steps.
288  */
289  for (int i=0; i<(8*L); i++, x += dx, y += dy) {
290  // time to change directions? (Note that this runs
291  // the first time through the loop, initializing dx,dy
292  // appropriately.)
293  if (i % (2*L) == 0) {
294  int leg = (i/(2*L));
295  // dx = [ 0, -1, 0, 1 ][leg]
296  dx = ( leg % 2) * (-1 + 2*(leg/2));
297  // dy = [ 1, 0, -1, 0 ][leg]
298  dy = ((leg+1) % 2) * ( 1 - 2*(leg/2));
299  }
300  //printf(" i=%i, leg=%i, dx=%i, dy=%i, x=%i, y=%i\n", i, leg, dx, dy, x, y);
301  int px = cx + x - ix0;
302  int py = cy + y - iy0;
303  // If the shadowing pixel is out of bounds, nothing to do.
304  if (px < 0 || px >= iW || py < 0 || py >= iH)
305  continue;
306  // The pixel casting the shadow
307  ImagePixelT pix = (*shadowingImg)(px,py);
308 
309  // Cast this pixel's shadow S pixels long in a cone.
310  // We compute the range of slopes (or inverse-slopes)
311  // shadowed by the pixel, [ds0,ds1]
312  double ds0, ds1;
313  // Range of slopes shadowed
314  const double A = 0.3;
315  int shx, shy;
316  int psx, psy;
317  // Are we traversing a vertical edge of the box?
318  if (dx == 0) {
319  // (if so, then "x" is +- L, so no div-by-zero)
320  ds0 = (double(y) / double(x)) - A;
321  ds1 = ds0 + 2.0 * A;
322  // cast the shadow on column x + sign(x)*shx
323  for (shx=1; shx<=S; shx++) {
324  int xsign = (x>0?1:-1);
325  // the column being shadowed
326  psx = cx + x + (xsign*shx) - ix0;
327  if (psx < 0 || psx >= iW)
328  continue;
329  // shadow covers a range of y values based on slope
330  for (shy = lround(shx * ds0);
331  shy <= lround(shx * ds1); shy++) {
332  psy = cy + y + xsign*shy - iy0;
333  if (psy < 0 || psy >= iH)
334  continue;
335  img(psx, psy) = std::min(img(psx, psy), pix);
336  }
337  }
338 
339  } else {
340  // We're traversing a horizontal edge of the box; y = +-L
341  ds0 = (double(x) / double(y)) - A;
342  ds1 = ds0 + 2.0 * A;
343  // Cast shadow on row y + sign(y)*shy
344  for (shy=1; shy<=S; shy++) {
345  int ysign = (y>0?1:-1);
346  psy = cy + y + (ysign*shy) - iy0;
347  if (psy < 0 || psy >= iH)
348  continue;
349  // shadow covers a range of x vals based on slope
350  for (shx = lround(shy * ds0);
351  shx <= lround(shy * ds1); shx++) {
352  psx = cx + x + ysign*shx - ix0;
353  if (psx < 0 || psx >= iW)
354  continue;
355  img(psx, psy) = std::min(img(psx, psy), pix);
356  }
357  }
358  }
359  }
360  }
361  shadowingImg->assign(img);
362  }
363 }
364 
365 static double _get_contrib_r_to_footprint(int x, int y,
366  PTR(det::Footprint) tfoot) {
367  double minr2 = 1e12;
368  for (afwGeom::Span const & sp : *(tfoot->getSpans())) {
369  int mindx;
370  // span is to right of pixel?
371  int dx = sp.getX0() - x;
372  if (dx >= 0) {
373  mindx = dx;
374  } else {
375  // span is to left of pixel?
376  dx = x - sp.getX1();
377  if (dx >= 0) {
378  mindx = dx;
379  } else {
380  // span contains pixel (in x direction)
381  mindx = 0;
382  }
383  }
384  int dy = sp.getY() - y;
385  minr2 = std::min(minr2, (double)(mindx*mindx + dy*dy));
386  }
387  //printf("stray flux at (%i,%i): dist to t %i is %g\n", x, y, (int)i, sqrt(minr2));
388  return 1. / (1. + minr2);
389 }
390 
391 
392 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
393 void
396  ImagePtrT tsum,
397  MaskedImageT const& img,
398  int strayFluxOptions,
399  std::vector<PTR(det::Footprint)> tfoots,
400  std::vector<bool> const& ispsf,
401  std::vector<int> const& pkx,
402  std::vector<int> const& pky,
403  double clipStrayFluxFraction,
405  ) {
406 
407  typedef typename det::HeavyFootprint<ImagePixelT, MaskPixelT, VariancePixelT> HeavyFootprint;
409 
410  // when doing stray flux: the footprints and pixels, which we'll
411  // combine into the return 'strays' HeavyFootprint at the end.
413  std::vector<std::vector<afwGeom::Span>> straySpans(tfoots.size());
417 
418  int ix0 = img.getX0();
419  int iy0 = img.getY0();
420  geom::Box2I sumbb = tsum->getBBox();
421  int sumx0 = sumbb.getMinX();
422  int sumy0 = sumbb.getMinY();
423 
424  for (size_t i=0; i<tfoots.size(); ++i) {
425  strayfoot.push_back(PTR(det::Footprint)());
427  straymask.push_back(std::vector<MaskPixelT>());
429  }
430 
431  bool always = (strayFluxOptions & STRAYFLUX_TO_POINT_SOURCES_ALWAYS);
432 
433  typedef std::uint16_t itype;
434  PTR(image::Image<itype>) nearest;
435 
436  if (strayFluxOptions & STRAYFLUX_NEAREST_FOOTPRINT) {
437  // Compute the map of which footprint is closest to each
438  // pixel in the bbox.
439  typedef std::uint16_t dtype;
440  PTR(image::Image<dtype>) dist(new image::Image<dtype>(sumbb));
441  nearest = PTR(image::Image<itype>)(new image::Image<itype>(sumbb));
442 
444  std::vector<PTR(det::Footprint)>* footlist = &tfoots;
445 
446  if (!always && ispsf.size()) {
447  // create a temp list that has empty footprints in place
448  // of all the point sources.
449  auto empty = std::make_shared<det::Footprint>();
450  empty->setPeakSchema(foot.getPeaks().getSchema());
451  for (size_t i=0; i<tfoots.size(); ++i) {
452  if (ispsf[i]) {
453  templist.push_back(empty);
454  } else {
455  templist.push_back(tfoots[i]);
456  }
457  }
458  footlist = &templist;
459  }
460  nearestFootprint(*footlist, nearest, dist);
461  }
462 
463  // Go through the (parent) Footprint looking for stray flux:
464  // pixels that are not claimed by any template, and positive.
465  for (afwGeom::Span const & s : *foot.getSpans()) {
466  int y = s.getY();
467  int x0 = s.getX0();
468  int x1 = s.getX1();
469  typename ImageT::x_iterator tsum_it =
470  tsum->row_begin(y - sumy0) + (x0 - sumx0);
471  typename MaskedImageT::x_iterator in_it =
472  img.row_begin(y - iy0) + (x0 - ix0);
473  double contrib[tfoots.size()];
474 
475  for (int x = x0; x <= x1; ++x, ++tsum_it, ++in_it) {
476  // Skip pixels that are covered by at least one
477  // template (*tsum_it > 0) or the input is not
478  // positive (*in_it <= 0).
479  if ((*tsum_it > 0) || (*in_it).image() <= 0) {
480  continue;
481  }
482 
483  if (strayFluxOptions & STRAYFLUX_R_TO_FOOTPRINT) {
484  // we'll compute these just-in-time
485  for (size_t i=0; i<tfoots.size(); ++i) {
486  contrib[i] = -1.0;
487  }
488  } else if (strayFluxOptions & STRAYFLUX_NEAREST_FOOTPRINT) {
489  for (size_t i=0; i<tfoots.size(); ++i) {
490  contrib[i] = 0.0;
491  }
492  int i = nearest->get0(x, y);
493  contrib[i] = 1.0;
494  } else {
495  // R_TO_PEAK
496  for (size_t i=0; i<tfoots.size(); ++i) {
497  // Split the stray flux by 1/(1+r^2) to peaks
498  int dx, dy;
499  dx = pkx[i] - x;
500  dy = pky[i] - y;
501  contrib[i] = 1. / (1. + dx*dx + dy*dy);
502  }
503  }
504 
505  // Round 1: skip point sources unless STRAYFLUX_TO_POINT_SOURCES_ALWAYS
506  // are we going to assign stray flux to ptsrcs?
507  bool ptsrcs = always;
508  double csum = 0.;
509  for (size_t i=0; i<tfoots.size(); ++i) {
510  // if we're skipping point sources and this is a point source...
511  if ((!ptsrcs) && ispsf.size() && ispsf[i]) {
512  continue;
513  }
514  if (contrib[i] == -1.0) {
515  contrib[i] = _get_contrib_r_to_footprint(x, y, tfoots[i]);
516  }
517  csum += contrib[i];
518  }
519  if ((csum == 0.) &&
520  (strayFluxOptions &
521  STRAYFLUX_TO_POINT_SOURCES_WHEN_NECESSARY)) {
522  // No extended sources -- assign to pt sources
523  //LOGL_DEBUG(_log, "necessary to assign stray flux to point sources");
524  ptsrcs = true;
525  for (size_t i=0; i<tfoots.size(); ++i) {
526  if (contrib[i] == -1.0) {
527  contrib[i] = _get_contrib_r_to_footprint(x, y, tfoots[i]);
528  }
529  csum += contrib[i];
530  }
531  }
532 
533  // Drop small contributions...
534  double strayclip = (clipStrayFluxFraction * csum);
535  csum = 0.;
536  for (size_t i=0; i<tfoots.size(); ++i) {
537  // skip ptsrcs?
538  if ((!ptsrcs) && ispsf.size() && ispsf[i]) {
539  contrib[i] = 0.;
540  continue;
541  }
542  // skip small contributions
543  if (contrib[i] < strayclip) {
544  contrib[i] = 0.;
545  continue;
546  }
547  csum += contrib[i];
548  }
549 
550  for (size_t i=0; i<tfoots.size(); ++i) {
551  if (contrib[i] == 0.) {
552  continue;
553  }
554  // the stray flux to give to template i
555  double p = (contrib[i] / csum) * (*in_it).image();
556 
557  if (!strayfoot[i]) {
558  strayfoot[i] = std::make_shared<det::Footprint>();
559  strayfoot[i]->setPeakSchema(foot.getPeaks().getSchema());
560  }
561  straySpans[i].push_back(afwGeom::Span(y, x, x));
562  straypix[i].push_back(p);
563  straymask[i].push_back((*in_it).mask());
564  strayvar[i].push_back((*in_it).variance());
565  }
566  }
567  }
568 
569  // Store the stray flux in HeavyFootprints
570  for (size_t i=0; i<tfoots.size(); ++i) {
571  if (strayfoot[i]) {
572  strayfoot[i]->setSpans(std::make_shared<afwGeom::SpanSet>(straySpans[i]));
573  }
574  if (!strayfoot[i]) {
575  strays.push_back(HeavyFootprintPtrT());
576  } else {
580  HeavyFootprintPtrT heavy(new HeavyFootprint(*strayfoot[i]));
581  ndarray::Array<ImagePixelT,1,1> himg = heavy->getImageArray();
588 
589  assert((size_t)strayfoot[i]->getArea() == straypix[i].size());
590 
591  for (spix = straypix[i].begin(),
592  smask = straymask[i].begin(),
593  svar = strayvar [i].begin(),
594  hpix = himg.begin(),
595  mpix = heavy->getMaskArray().begin(),
596  vpix = heavy->getVarianceArray().begin();
597  spix != straypix[i].end();
598  ++spix, ++smask, ++svar, ++hpix, ++mpix, ++vpix) {
599  *hpix = *spix;
600  *mpix = *smask;
601  *vpix = *svar;
602  }
603  strays.push_back(heavy);
604  }
605  }
606 }
607 
608 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
609 void
612  ImagePtrT tsum) {
613  geom::Box2I sumbb = tsum->getBBox();
614  int sumx0 = sumbb.getMinX();
615  int sumy0 = sumbb.getMinY();
616 
617  // Compute tsum = the sum of templates
618  for (size_t i=0; i<timgs.size(); ++i) {
619  ImagePtrT timg = timgs[i];
620  geom::Box2I tbb = timg->getBBox();
621  int tx0 = tbb.getMinX();
622  int ty0 = tbb.getMinY();
623  // To handle "ramped" templates that can extend outside the
624  // parent, clip the bbox. Note that we saved tx0,ty0 BEFORE
625  // doing this!
626  tbb.clip(sumbb);
627  int copyx0 = tbb.getMinX();
628  // Here we iterate over the template bbox -- we could instead
629  // iterate over the "tfoot"s.
630  for (int y=tbb.getMinY(); y<=tbb.getMaxY(); ++y) {
631  typename ImageT::x_iterator in_it = timg->row_begin(y - ty0) + (copyx0 - tx0);
632  typename ImageT::x_iterator inend = in_it + tbb.getWidth();
633  typename ImageT::x_iterator tsum_it =
634  tsum->row_begin(y - sumy0) + (copyx0 - sumx0);
635  for (; in_it != inend; ++in_it, ++tsum_it) {
636  *tsum_it += std::max((ImagePixelT)0., static_cast<ImagePixelT>(*in_it));
637  }
638  }
639  }
640 
641 }
642 
691 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
695  det::Footprint const& foot,
697  std::vector<PTR(det::Footprint)> tfoots,
698  ImagePtrT tsum,
699  std::vector<bool> const& ispsf,
700  std::vector<int> const& pkx,
701  std::vector<int> const& pky,
703  int strayFluxOptions,
704  double clipStrayFluxFraction
705  ) {
706 
707  if (timgs.size() != tfoots.size()) {
709  (boost::format("Template images must be the same length as template footprints (%d vs %d)")
710  % timgs.size() % tfoots.size()).str());
711  }
712 
713  for (size_t i=0; i<timgs.size(); ++i) {
714  if (!timgs[i]->getBBox().contains(tfoots[i]->getBBox())) {
716  "Template image MUST contain template footprint");
717  }
718  if (!img.getBBox().contains(foot.getBBox())) {
720  "Image bbox MUST contain parent footprint");
721  }
722  // template bounding-boxes *can* extend outside the parent
723  // footprint if we are ramping templates with significant flux
724  // at the edges. We handle this below.
725  }
726 
727  // the apportioned flux return value
729 
730  LOG_LOGGER _log = LOG_GET("meas.deblender.apportionFlux");
731  bool findStrayFlux = (strayFluxOptions & ASSIGN_STRAYFLUX);
732 
733  int ix0 = img.getX0();
734  int iy0 = img.getY0();
735  geom::Box2I fbb = foot.getBBox();
736 
737  if (!tsum) {
738  tsum = ImagePtrT(new ImageT(fbb.getDimensions()));
739  tsum->setXY0(fbb.getMinX(), fbb.getMinY());
740  }
741 
742  if (!tsum->getBBox().contains(foot.getBBox())) {
744  "Template sum image MUST contain parent footprint");
745  }
746 
747  geom::Box2I sumbb = tsum->getBBox();
748  int sumx0 = sumbb.getMinX();
749  int sumy0 = sumbb.getMinY();
750 
751  _sum_templates(timgs, tsum);
752 
753  // Compute flux portions
754  for (size_t i=0; i<timgs.size(); ++i) {
755  ImagePtrT timg = timgs[i];
756  // Initialize return value:
757  MaskedImagePtrT port(new MaskedImageT(timg->getDimensions()));
758  port->setXY0(timg->getXY0());
759  portions.push_back(port);
760 
761  // Split flux = image * template / tsum
762  geom::Box2I tbb = timg->getBBox();
763  int tx0 = tbb.getMinX();
764  int ty0 = tbb.getMinY();
765  // As above
766  tbb.clip(sumbb);
767  int copyx0 = tbb.getMinX();
768  for (int y=tbb.getMinY(); y<=tbb.getMaxY(); ++y) {
769  typename MaskedImageT::x_iterator in_it =
770  img.row_begin(y - iy0) + (copyx0 - ix0);
771  typename ImageT::x_iterator tptr =
772  timg->row_begin(y - ty0) + (copyx0 - tx0);
773  typename ImageT::x_iterator tend = tptr + tbb.getWidth();
774  typename ImageT::x_iterator tsum_it =
775  tsum->row_begin(y - sumy0) + (copyx0 - sumx0);
776  typename MaskedImageT::x_iterator out_it =
777  port->row_begin(y - ty0) + (copyx0 - tx0);
778  for (; tptr != tend; ++tptr, ++in_it, ++out_it, ++tsum_it) {
779  if (*tsum_it == 0) {
780  continue;
781  }
782  double frac = std::max((ImagePixelT)0., static_cast<ImagePixelT>(*tptr)) / (*tsum_it);
783  //if (frac == 0) {
784  // treat mask planes differently?
785  // }
786  out_it.mask() = (*in_it).mask();
787  out_it.variance() = (*in_it).variance();
788  out_it.image() = (*in_it).image() * frac;
789  }
790  }
791  }
792 
793  if (findStrayFlux) {
794  if ((ispsf.size() > 0) && (ispsf.size() != timgs.size())) {
796  (boost::format("'ispsf' must be the same length as templates (%d vs %d)")
797  % ispsf.size() % timgs.size()).str());
798  }
799  if ((pkx.size() != timgs.size()) || (pky.size() != timgs.size())) {
801  (boost::format("'pkx' and 'pky' must be the same length as templates (%d,%d vs %d)")
802  % pkx.size() % pky.size() % timgs.size()).str());
803  }
804  _find_stray_flux(foot, tsum, img, strayFluxOptions, tfoots,
805  ispsf, pkx, pky, clipStrayFluxFraction, strays);
806  }
807  return portions;
808 }
809 
810 
820 public:
822 
824 
826  SpanSet const& arr,
827  int cx, int cy, bool forward=true)
828  : _real(real), _cx(cx), _cy(cy), _forward(forward)
829  {
830  if (_forward) {
831  _end = arr.end();
832  } else {
833  _end = arr.begin();
834  }
835  }
836 
838  return _real == other;
839  }
841  return _real != other;
842  }
844  return _real <= other;
845  }
847  return _real < other;
848  }
850  return _real >= other;
851  }
853  return _real > other;
854  }
855 
857  return (_real == other._real) &&
858  (_cx == other._cx) && (_cy == other._cy) &&
859  (_forward == other._forward);
860  }
862  return !(*this == other);
863  }
864 
866  if (_forward) {
867  _real++;
868  } else {
869  _real--;
870  }
871  return *this;
872  }
875  ++(*this);
876  return result;
877  }
878 
879  bool notDone() {
880  if (_forward) {
881  return _real < _end;
882  } else {
883  return _real >= _end;
884  }
885  }
886 
887  int dxlo() {
888  if (_forward) {
889  return _real->getX0() - _cx;
890  } else {
891  return _cx - _real->getX1();
892  }
893  }
894  int dxhi() {
895  if (_forward) {
896  return _real->getX1() - _cx;
897  } else {
898  return _cx - _real->getX0();
899  }
900  }
901  int dy() {
902  return std::abs(_real->getY() - _cy);
903  }
904  int x0() {
905  return _real->getX0();
906  }
907  int x1() {
908  return _real->getX1();
909  }
910  int y() {
911  return _real->getY();
912  }
913 
914 private:
917  int _cx;
918  int _cy;
919  bool _forward;
920 };
921 
922 
923 /*
924  // Check symmetrizeFootprint by computing truth naively.
925  // compute correct answer dumbly
926  det::Footprint truefoot;
927  geom::Box2I bbox = foot.getBBox();
928  for (int y=bbox.getMinY(); y<=bbox.getMaxY(); y++) {
929  for (int x=bbox.getMinX(); x<=bbox.getMaxX(); x++) {
930  int dy = y - cy;
931  int dx = x - cx;
932  int x2 = cx - dx;
933  int y2 = cy - dy;
934  if (foot.contains(geom::Point2I(x, y)) &&
935  foot.contains(geom::Point2I(x2, y2))) {
936  truefoot.addSpan(y, x, x);
937  }
938  }
939  }
940  truefoot.normalize();
941 
942  SpanList sp1 = truefoot.getSpans();
943  SpanList sp2 = sfoot->getSpans();
944  for (SpanList::const_iterator spit1 = sp1.begin(),
945  spit2 = sp2.begin();
946  spit1 != sp1.end() && spit2 != sp2.end();
947  spit1++, spit2++) {
948  //printf("\n");
949  printf(" true y %i, x [%i, %i]\n", (*spit1)->getY(), (*spit1)->getX0(), (*spit1)->getX1());
950  printf(" sfoot y %i, x [%i, %i]\n", (*spit2)->getY(), (*spit2)->getX0(), (*spit2)->getX1());
951  if (((*spit1)->getY() != (*spit2)->getY()) ||
952  ((*spit1)->getX0() != (*spit2)->getX0()) ||
953  ((*spit1)->getX1() != (*spit2)->getX1())) {
954  printf("*******************************************\n");
955  }
956  }
957  assert(sp1.size() == sp2.size());
958  for (SpanList::const_iterator spit1 = sp1.begin(),
959  spit2 = sp2.begin();
960  spit1 != sp1.end() && spit2 != sp2.end();
961  spit1++, spit2++) {
962  assert((*spit1)->getY() == (*spit2)->getY());
963  assert((*spit1)->getX0() == (*spit2)->getX0());
964  assert((*spit1)->getX1() == (*spit2)->getX1());
965  }
966  */
967 
973 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
977  det::Footprint const& foot,
978  int cx, int cy) {
979 
980  auto sfoot = std::make_shared<det::Footprint>();
981  sfoot->setPeakSchema(foot.getPeaks().getSchema());
982  afwGeom::SpanSet const & spans = *foot.getSpans();
983 
984  LOG_LOGGER _log = LOG_GET("meas.deblender.symmetrizeFootprint");
985 
986  // Find the Span containing the peak.
987  afwGeom::Span target(cy, cx, cx);
989  std::upper_bound(spans.begin(), spans.end(), target, span_compare);
990  // upper_bound returns the last position where "target" could be inserted;
991  // ie, the first Span larger than "target". The Span containing "target"
992  // should be peakspan-1 or (if the peak is on the first pixel in the span,
993  // peakspan.
994  afwGeom::Span sp;
995  if (peakspan == spans.begin()) {
996  sp = *peakspan;
997  if (!sp.contains(cx, cy)) {
998  LOGL_WARN(_log,
999  "Failed to find span containing (%i,%i): before the beginning of this footprint", cx, cy);
1000  return PTR(det::Footprint)();
1001  }
1002  } else {
1003  --peakspan;
1004  sp = *peakspan;
1005 
1006  if (!sp.contains(cx, cy)) {
1007  ++peakspan;
1008  sp = *peakspan;
1009  if (!sp.contains(cx, cy)) {
1010  geom::Box2I fbb = foot.getBBox();
1011  LOGL_WARN(_log, "Failed to find span containing (%i,%i): nearest is %i, [%i,%i]. "
1012  "Footprint bbox is [%i,%i],[%i,%i]",
1013  cx, cy, sp.getY(), sp.getX0(), sp.getX1(),
1014  fbb.getMinX(), fbb.getMaxX(), fbb.getMinY(), fbb.getMaxY());
1015  return PTR(det::Footprint)();
1016  }
1017  }
1018  }
1019  LOGL_DEBUG(_log, "Span containing (%i,%i): (x=[%i,%i], y=%i)",
1020  cx, cy, sp.getX0(), sp.getX1(), sp.getY());
1021 
1022  // The symmetric templates are essentially an AND of the footprint
1023  // pixels and its 180-degree-rotated self, rotated around the
1024  // peak (cx,cy).
1025  //
1026  // We iterate forward and backward simultaneously, starting from
1027  // the span containing the peak and moving out, row by row.
1028  //
1029  // In the loop below, we search for the next pair of Spans that
1030  // overlap (in "dx" from the center), output the overlapping
1031  // portion of the Spans, and advance either the "fwd" or "back"
1032  // iterator. When we fail to find an overlapping pair of Spans,
1033  // we move on to the next row.
1034  //
1035  // [The following paragraph is somewhat obsoleted by the
1036  // RelativeSpanIterator class, which performs some of the renaming
1037  // and the dx,dy coords.]
1038  //
1039  // '''In reading the code, "forward", "advancing", etc, are all
1040  // from the perspective of the "fwd" iterator (the one going
1041  // forward through the Span list, from low to high Y and then low
1042  // to high X). It will help to imagine making a copy of the
1043  // footprint and rotating it around the center pixel by 180
1044  // degrees, so that "fwd" and "back" are both iterating the same
1045  // direction; we're then just finding the AND of those two
1046  // iterators, except we have to work in dx,dy coordinates rather
1047  // than original x,y coords, and the accessors for "back" are
1048  // opposite.'''
1049 
1050  RelativeSpanIterator fwd (peakspan, spans, cx, cy, true);
1051  RelativeSpanIterator back(peakspan, spans, cx, cy, false);
1052 
1053  int dy = 0;
1054  std::vector<afwGeom::Span> tmpSpans;
1055  while (fwd.notDone() && back.notDone()) {
1056  // forward and backward "y"; just symmetric around cy
1057  int fy = cy + dy;
1058  int by = cy - dy;
1059  // delta-x of the beginnings of the spans, for "fwd" and "back"
1060  int fdxlo = fwd.dxlo();
1061  int bdxlo = back.dxlo();
1062 
1063  // First find:
1064  // fend -- first span in the next row, or end(); ie,
1065  // the end of this row in the forward direction
1066  // bend -- the end of this row in the backward direction
1067  RelativeSpanIterator fend, bend;
1068  for (fend = fwd; fend.notDone(); ++fend) {
1069  if (fend.dy() != dy)
1070  break;
1071  }
1072  for (bend = back; bend.notDone(); ++bend) {
1073  if (bend.dy() != dy)
1074  break;
1075  }
1076 
1077  LOGL_DEBUG(_log, "dy=%i, fy=%i, fx=[%i, %i], by=%i, fx=[%i, %i], fdx=%i, bdx=%i",
1078  dy, fy, fwd.x0(), fwd.x1(), by, back.x0(), back.x1(),
1079  fdxlo, bdxlo);
1080 
1081  // Find possibly-overlapping span
1082  if (bdxlo > fdxlo) {
1083  LOGL_DEBUG(_log, "Advancing forward.");
1084  // While the "forward" span is entirely to the "left" of the "backward" span,
1085  // (in dx coords), ie, |---fwd---X X---back---|
1086  // and we are comparing the edges marked X
1087  while ((fwd != fend) && (fwd.dxhi() < bdxlo)) {
1088  fwd++;
1089  if (fwd == fend) {
1090  LOGL_DEBUG(_log, "Reached fend");
1091  } else {
1092  LOGL_DEBUG(_log, "Advanced to forward span %i, [%i, %i]",
1093  fy, fwd.x0(), fwd.x1());
1094  }
1095  }
1096  } else if (fdxlo > bdxlo) {
1097  LOGL_DEBUG(_log, "Advancing backward.");
1098  // While the "backward" span is entirely to the "left" of the "foreward" span,
1099  // (in dx coords), ie, |---back---X X---fwd---|
1100  // and we are comparing the edges marked X
1101  while ((back != bend) && (back.dxhi() < fdxlo)) {
1102  back++;
1103  if (back == bend) {
1104  LOGL_DEBUG(_log, "Reached bend");
1105  } else {
1106  LOGL_DEBUG(_log, "Advanced to backward span %i, [%i, %i]",
1107  by, back.x0(), back.x1());
1108  }
1109  }
1110  }
1111 
1112  if ((back == bend) || (fwd == fend)) {
1113  // We reached the end of the row without finding spans that could
1114  // overlap. Move onto the next dy.
1115  if (back == bend) {
1116  LOGL_DEBUG(_log, "Reached bend");
1117  }
1118  if (fwd == fend) {
1119  LOGL_DEBUG(_log, "Reached fend");
1120  }
1121  back = bend;
1122  fwd = fend;
1123  dy++;
1124  continue;
1125  }
1126 
1127  // Spans may overlap -- find the overlapping part.
1128  int dxlo = std::max(fwd.dxlo(), back.dxlo());
1129  int dxhi = std::min(fwd.dxhi(), back.dxhi());
1130  if (dxlo <= dxhi) {
1131  LOGL_DEBUG(_log, "Adding span fwd %i, [%i, %i], back %i, [%i, %i]",
1132  fy, cx+dxlo, cx+dxhi, by, cx-dxhi, cx-dxlo);
1133  tmpSpans.push_back(afwGeom::Span(fy, cx + dxlo, cx + dxhi));
1134  tmpSpans.push_back(afwGeom::Span(by, cx - dxhi, cx - dxlo));
1135  }
1136 
1137  // Advance the one whose "hi" edge is smallest
1138  if (fwd.dxhi() < back.dxhi()) {
1139  fwd++;
1140  if (fwd == fend) {
1141  LOGL_DEBUG(_log, "Stepped to fend");
1142  } else {
1143  LOGL_DEBUG(_log, "Stepped forward to span %i, [%i, %i]",
1144  fwd.y(), fwd.x0(), fwd.x1());
1145  }
1146  } else {
1147  back++;
1148  if (back == bend) {
1149  LOGL_DEBUG(_log, "Stepped to bend");
1150  } else {
1151  LOGL_DEBUG(_log, "Stepped backward to span %i, [%i, %i]",
1152  back.y(), back.x0(), back.x1());
1153  }
1154  }
1155 
1156  if ((back == bend) || (fwd == fend)) {
1157  // Reached the end of the row. On to the next dy!
1158  if (back == bend) {
1159  LOGL_DEBUG(_log, "Reached bend");
1160  }
1161  if (fwd == fend) {
1162  LOGL_DEBUG(_log, "Reached fend");
1163  }
1164  back = bend;
1165  fwd = fend;
1166  dy++;
1167  continue;
1168  }
1169 
1170  }
1171  sfoot->setSpans(std::make_shared<afwGeom::SpanSet>(std::move(tmpSpans)));
1172  return sfoot;
1173 }
1174 
1187 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
1192  MaskedImageT const& img,
1193  det::Footprint const& foot,
1194  det::PeakRecord const& peak,
1195  double sigma1,
1196  bool minZero,
1197  bool patchEdge,
1198  bool* patchedEdges) {
1199 
1200  typedef typename MaskedImageT::const_xy_locator xy_loc;
1201 
1202  *patchedEdges = false;
1203 
1204  int cx = peak.getIx();
1205  int cy = peak.getIy();
1206 
1207  LOG_LOGGER _log = LOG_GET("meas.deblender.symmetricFootprint");
1208 
1209  if (!img.getBBox(image::PARENT).contains(foot.getBBox())) {
1210  throw LSST_EXCEPT(lsst::pex::exceptions::LengthError, "Image too small for footprint");
1211  }
1212 
1213  FootprintPtrT sfoot = symmetrizeFootprint(foot, cx, cy);
1214 
1215  if (!sfoot) {
1217  }
1218 
1219  if (!img.getBBox(image::PARENT).contains(sfoot->getBBox())) {
1221  "Image too small for symmetrized footprint");
1222  }
1223  afwGeom::SpanSet const & spans = *sfoot->getSpans();
1224 
1225  // does this footprint touch an EDGE?
1226  bool touchesEdge = false;
1227  if (patchEdge) {
1228  LOGL_DEBUG(_log, "Checking footprint for EDGE bits");
1229  MaskPtrT mask = img.getMask();
1230  bool edge = false;
1231  MaskPixelT edgebit = mask->getPlaneBitMask("EDGE");
1232  for (afwGeom::SpanSet::const_iterator fwd=spans.begin();
1233  fwd != spans.end(); ++fwd) {
1234  int x0 = fwd->getX0();
1235  int x1 = fwd->getX1();
1236  typename MaskT::x_iterator xiter =
1237  mask->x_at(x0 - mask->getX0(), fwd->getY() - mask->getY0());
1238  for (int x=x0; x<=x1; ++x, ++xiter) {
1239  if ((*xiter) & edgebit) {
1240  edge = true;
1241  break;
1242  }
1243  }
1244  if (edge)
1245  break;
1246  }
1247  if (edge) {
1248  LOGL_DEBUG(_log, "Footprint includes an EDGE pixel.");
1249  touchesEdge = true;
1250  }
1251  }
1252 
1253  // The result image:
1254  ImagePtrT targetimg(new ImageT(sfoot->getBBox()));
1255 
1257  afwGeom::SpanSet::const_iterator back = spans.end()-1;
1258 
1259  ImagePtrT theimg = img.getImage();
1260 
1261  for (; fwd <= back; fwd++, back--) {
1262  int fy = fwd->getY();
1263  int by = back->getY();
1264 
1265  for (int fx=fwd->getX0(), bx=back->getX1();
1266  fx <= fwd->getX1();
1267  fx++, bx--) {
1268  // FIXME -- CURRENTLY WE IGNORE THE MASK PLANE! options
1269  // include ORing the mask bits, or being clever about
1270  // ignoring some masked pixels, or copying the mask bits
1271  // of the min pixel
1272 
1273  // We have already checked the bounding box, so this should always be satisfied
1274  assert(theimg->getBBox(image::PARENT).contains(geom::Point2I(fx, fy)));
1275  assert(theimg->getBBox(image::PARENT).contains(geom::Point2I(bx, by)));
1276 
1277  // FIXME -- we could do this with image iterators instead.
1278  // But first profile to show that it's necessary and an
1279  // improvement.
1280  ImagePixelT pixf = theimg->get0(fx, fy);
1281  ImagePixelT pixb = theimg->get0(bx, by);
1282  ImagePixelT pix = std::min(pixf, pixb);
1283  if (minZero) {
1284  pix = std::max(pix, static_cast<ImagePixelT>(0));
1285  }
1286  targetimg->set0(fx, fy, pix);
1287  targetimg->set0(bx, by, pix);
1288 
1289  }
1290  }
1291 
1292  if (touchesEdge) {
1293  // Find spans whose mirrors fall outside the image bounds,
1294  // grow the footprint to include those spans, and plug in
1295  // their pixel values.
1296  geom::Box2I bb = sfoot->getBBox();
1297 
1298  // Actually, it's not necessarily the IMAGE bounds that count
1299  //-- the footprint may not go right to the image edge.
1300  //geom::Box2I imbb = img.getBBox();
1301  geom::Box2I imbb = foot.getBBox();
1302 
1303  LOGL_DEBUG(_log, "Footprint touches EDGE: start bbox [%i,%i],[%i,%i]",
1304  bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY());
1305  // original footprint spans
1306  const afwGeom::SpanSet & ospans = *foot.getSpans();
1307  for (fwd = ospans.begin(); fwd != ospans.end(); ++fwd) {
1308  int y = fwd->getY();
1309  int x = fwd->getX0();
1310  // mirrored coords
1311  int ym = cy + (cy - y);
1312  int xm = cx + (cx - x);
1313  if (!imbb.contains(geom::Point2I(xm, ym))) {
1314  bb.include(geom::Point2I(x, y));
1315  }
1316  x = fwd->getX1();
1317  xm = cx + (cx - x);
1318  if (!imbb.contains(geom::Point2I(xm, ym))) {
1319  bb.include(geom::Point2I(x, y));
1320  }
1321  }
1322  LOGL_DEBUG(_log, "Footprint touches EDGE: grown bbox [%i,%i],[%i,%i]",
1323  bb.getMinX(), bb.getMaxX(), bb.getMinY(), bb.getMaxY());
1324 
1325  // New template image
1326  ImagePtrT targetimg2(new ImageT(bb));
1327  sfoot->getSpans()->copyImage(*targetimg, *targetimg2);
1328 
1329  LOGL_DEBUG(_log, "Symmetric footprint spans:");
1330  const afwGeom::SpanSet & sspans = *sfoot->getSpans();
1331  for (fwd = sspans.begin(); fwd != sspans.end(); ++fwd) {
1332  LOGL_DEBUG(_log, " %s", fwd->toString().c_str());
1333  }
1334 
1335  // copy original 'img' pixels for the portion of spans whose
1336  // mirrors are out of bounds.
1337  std::vector<afwGeom::Span> newSpans(sfoot->getSpans()->begin(), sfoot->getSpans()->end());
1338  for (fwd = ospans.begin(); fwd != ospans.end(); ++fwd) {
1339  int y = fwd->getY();
1340  int x0 = fwd->getX0();
1341  int x1 = fwd->getX1();
1342  // mirrored coords
1343  int ym = cy + (cy - y);
1344  int xm0 = cx + (cx - x0);
1345  int xm1 = cx + (cx - x1);
1346  bool in0 = imbb.contains(geom::Point2I(xm0, ym));
1347  bool in1 = imbb.contains(geom::Point2I(xm1, ym));
1348  if (in0 && in1) {
1349  // both endpoints of the symmetric span are in bounds; nothing to do
1350  continue;
1351  }
1352  // clip to the part of the span where the mirror is out of bounds
1353  if (in0) {
1354  // the mirror of x0 is in-bounds; move x0 to be the first pixel
1355  // whose mirror would be out-of-bounds
1356  x0 = cx + (cx - (imbb.getMinX() - 1));
1357  }
1358  if (in1) {
1359  x1 = cx + (cx - (imbb.getMaxX() + 1));
1360  }
1361  LOGL_DEBUG(_log, "Span y=%i, x=[%i,%i] has mirror (%i,[%i,%i]) out-of-bounds; clipped to %i,[%i,%i]",
1362  y, fwd->getX0(), fwd->getX1(), ym, xm1, xm0, y, x0, x1);
1363  typename MaskedImageT::x_iterator initer =
1364  img.x_at(x0 - img.getX0(), y - img.getY0());
1365  typename ImageT::x_iterator outiter =
1366  targetimg2->x_at(x0 - targetimg2->getX0(), y - targetimg2->getY0());
1367  for (int x=x0; x<=x1; ++x, ++outiter, ++initer) {
1368  *outiter = initer.image();
1369  }
1370  newSpans.push_back(afwGeom::Span(y, x0, x1));
1371  }
1372  sfoot->setSpans(std::make_shared<afwGeom::SpanSet>(std::move(newSpans)));
1373  targetimg = targetimg2;
1374  }
1375 
1376  *patchedEdges = touchesEdge;
1377  return std::pair<ImagePtrT, FootprintPtrT>(targetimg, sfoot);
1378 }
1379 
1384 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
1385 bool
1388  PTR(det::Footprint) sfoot,
1389  ImagePixelT thresh) {
1390 
1391  LOG_LOGGER _log = LOG_GET("meas.deblender.hasSignificantFluxAtEdge");
1392 
1393  // Find edge template pixels with significant flux -- perhaps
1394  // because their symmetric pixels were outside the footprint?
1395  // (clipped by an image edge, etc)
1396  std::shared_ptr<afwGeom::SpanSet> spans = sfoot->getSpans()->findEdgePixels();
1397 
1398  for (afwGeom::SpanSet::const_iterator sp = spans->begin(); sp != spans->end(); ++sp) {
1399  int const y = sp->getY();
1400  int const x0 = sp->getX0();
1401  int const x1 = sp->getX1();
1402  int x;
1403  typename ImageT::const_x_iterator xiter;
1404  for (xiter = img->x_at(x0 - img->getX0(), y - img->getY0()), x=x0; x<=x1; ++x, ++xiter) {
1405  if (*xiter >= thresh) {
1406  return true;
1407  }
1408  }
1409  }
1410  return false;
1411 }
1412 
1417 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
1421  PTR(det::Footprint) sfoot,
1422  ImagePixelT thresh) {
1423  LOG_LOGGER _log = LOG_GET("meas.deblender.getSignificantEdgePixels");
1424 
1425 
1426  auto significant = std::make_shared<det::Footprint>();
1427  significant->setPeakSchema(sfoot->getPeaks().getSchema());
1428 
1429  int const x0 = img->getX0(), y0 = img->getY0();
1430  std::shared_ptr<afwGeom::SpanSet> edgeSpans = sfoot->getSpans()->findEdgePixels();
1431  std::vector<afwGeom::Span> tmpSpans;
1432  for (afwGeom::SpanSet::const_iterator ss = edgeSpans->begin(); ss != edgeSpans->end(); ++ss) {
1433  afwGeom::Span const& span = *ss;
1434  int const y = span.getY();
1435  int x = span.getX0();
1436  typename ImageT::const_x_iterator iter = img->x_at(x - x0, y - y0);
1437  bool onSpan = false; // Are we in a span of interest
1438  int xSpan; // Starting x of span
1439  for (; x <= span.getX1(); ++x, ++iter) {
1440  if (*iter >= thresh) {
1441  onSpan = true;
1442  xSpan = x;
1443  } else if (onSpan) {
1444  onSpan = false;
1445  tmpSpans.push_back(afwGeom::Span(y, xSpan, x - 1));
1446  }
1447  }
1448  if (onSpan) {
1449  tmpSpans.push_back(afwGeom::Span(y, xSpan, span.getX1()));
1450  }
1451  }
1452  significant->setSpans(std::make_shared<afwGeom::SpanSet>(std::move(tmpSpans)));
1453  return significant;
1454 }
1455 
1456 
1457 // Instantiate
1458 template class deblend::BaselineUtils<float>;
static bool hasSignificantFluxAtEdge(ImagePtrT, std::shared_ptr< lsst::afw::detection::Footprint >, ImagePixelT threshold)
Returns true if the given Footprint sfoot in image img has flux above value thresh at its edge...
Extent2I const getDimensions() const noexcept
Definition: Box.h:186
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
Angle abs(Angle const &a)
Definition: Angle.h:106
int getIy() const
Convenience accessors for the keys in the minimal schema.
Definition: Peak.h:231
#define LOG_LOGGER
Definition: Log.h:703
afw::table::Key< afw::table::Array< ImagePixelT > > image
Key< Flag > const & target
static std::shared_ptr< lsst::afw::detection::Footprint > getSignificantEdgePixels(ImagePtrT, std::shared_ptr< lsst::afw::detection::Footprint >, ImagePixelT threshold)
Returns a list of pixels that are on the edge of the given Footprint sfoot* in image img...
afw::table::Key< afw::table::Array< MaskPixelT > > mask
bool operator<(const SpanSet::const_iterator &other)
const_iterator end() const
Definition: SpanSet.h:89
A compact representation of a collection of pixels.
Definition: SpanSet.h:77
A range of pixels within one row of an Image.
Definition: Span.h:47
int getHeight() const
Return the number of rows in the image.
Definition: ImageBase.h:335
Reports attempts to exceed implementation-defined length limits for some classes. ...
Definition: Runtime.h:76
_const_view_t::x_iterator const_x_iterator
A const iterator for traversing the pixels in a row.
Definition: ImageBase.h:141
bool operator==(RelativeSpanIterator &other)
T upper_bound(T... args)
bool operator!=(const SpanSet::const_iterator &other)
_view_t::x_iterator x_iterator
An iterator for traversing the pixels in a row.
Definition: ImageBase.h:133
int y
Definition: SpanSet.cc:49
static void _find_stray_flux(lsst::afw::detection::Footprint const &foot, ImagePtrT tsum, MaskedImageT const &img, int strayFluxOptions, std::vector< std::shared_ptr< lsst::afw::detection::Footprint > > tfoots, std::vector< bool > const &ispsf, std::vector< int > const &pkx, std::vector< int > const &pky, double clipStrayFluxFraction, std::vector< std::shared_ptr< typename lsst::afw::detection::HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > > > &strays)
boost::shared_ptr< lsst::afw::detection::Footprint > FootprintPtrT
Definition: BaselineUtils.h:33
bool operator==(const SpanSet::const_iterator &other)
T end(T... args)
const_iterator begin() const
Definition: SpanSet.h:88
Schema getSchema() const
Return the schema associated with the catalog&#39;s table.
Definition: Catalog.h:117
ItemVariant const * other
Definition: Schema.cc:56
RelativeSpanIterator(SpanSet::const_iterator const &real, SpanSet const &arr, int cx, int cy, bool forward=true)
int getX0() const
Return the image&#39;s column-origin.
Definition: ImageBase.h:343
static boost::shared_ptr< lsst::afw::detection::Footprint > symmetrizeFootprint(lsst::afw::detection::Footprint const &foot, int cx, int cy)
Given a Footprint foot and peak cx,cy, returns a Footprint that is symmetric around the peak (with tw...
x_iterator row_begin(int y) const
Return an x_iterator to the start of the y&#39;th row.
Definition: ImageBase.h:438
#define LOGL_DEBUG(logger, message...)
Log a debug-level message using a varargs/printf style interface.
Definition: Log.h:504
x_iterator x_at(int x, int y) const
Return an x_iterator at the point (x, y)
Definition: MaskedImage.h:1219
A set of pixels in an Image, including those pixels&#39; actual values.
ImagePtr getImage() const
Return a (shared_ptr to) the MaskedImage&#39;s image.
Definition: MaskedImage.h:1057
LSST DM logging module built on log4cxx.
lsst::geom::Box2I getBBox() const
Return the Footprint&#39;s bounding box.
Definition: Footprint.h:208
An iterator to the MaskedImage.
Definition: MaskedImage.h:99
T min(T... args)
FastFinder::Iterator Iterator
Definition: FastFinder.cc:179
T push_back(T... args)
static void medianFilter(ImageT const &img, ImageT &outimg, int halfsize)
Run a spatial median filter over the given input img, writing the results to out. ...
bool operator>=(const SpanSet::const_iterator &other)
int getX0() const
Return the image&#39;s column-origin.
Definition: MaskedImage.h:1105
boost::shared_ptr< lsst::afw::image::Mask< MaskPixelT > > MaskPtrT
Definition: BaselineUtils.h:30
int getMaxY() const noexcept
Definition: Box.h:162
x_iterator row_begin(int y) const
Return an x_iterator to the start of the image.
Definition: MaskedImage.cc:612
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:73
int getWidth() const noexcept
Definition: Box.h:187
#define LOGL_WARN(logger, message...)
Log a warn-level message using a varargs/printf style interface.
Definition: Log.h:536
Ref< ImagePixelT >::type image()
Return (a reference to) the image part of the Pixel pointed at by the iterator.
Definition: MaskedImage.h:139
int getIx() const
Convenience accessors for the keys in the minimal schema.
Definition: Peak.h:230
T max(T... args)
T move(T... args)
int getMaxX() const noexcept
Definition: Box.h:161
Class to describe the properties of a detected object from an image.
Definition: Footprint.h:62
double x
MaskPtr getMask() const
Return a (shared_ptr to) the MaskedImage&#39;s mask.
Definition: MaskedImage.h:1069
int getMinX() const noexcept
Definition: Box.h:157
static void makeMonotonic(ImageT &img, lsst::afw::detection::PeakRecord const &pk)
Given an image mimg and Peak location peak, overwrite mimg so that pixels further from the peak have ...
int getY0() const
Return the image&#39;s row-origin.
Definition: MaskedImage.h:1113
T size(T... args)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
STL class.
def apportionFlux(debResult, log, assignStrayFlux=True, strayFluxAssignment='r-to-peak', strayFluxToPointSources='necessary', clipStrayFluxFraction=0.001, getTemplateSum=False)
Definition: plugins.py:1399
std::vector< Span >::const_iterator const_iterator
Definition: SpanSet.h:80
int getY0() const
Return the image&#39;s row-origin.
Definition: ImageBase.h:351
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
Definition: Box.cc:152
This is a convenience class used in symmetrizeFootprint, wrapping the idea of iterating through a Spa...
static void _sum_templates(std::vector< ImagePtrT > timgs, ImagePtrT tsum)
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Definition: Box.cc:114
int getWidth() const
Return the number of columns in the image.
Definition: ImageBase.h:333
void clip(Box2I const &other) noexcept
Shrink this to ensure that other.contains(*this).
Definition: Box.cc:189
x_iterator row_end(int y) const
Return an x_iterator to the end of the y&#39;th row.
Definition: ImageBase.h:441
bool operator!=(RelativeSpanIterator &other)
T nth_element(T... args)
std::shared_ptr< geom::SpanSet > getSpans() const
Return a shared pointer to the SpanSet.
Definition: Footprint.h:115
int getX1() const noexcept
Return the ending x-value.
Definition: Span.h:74
int getY() const noexcept
Return the y-value.
Definition: Span.h:76
static std::pair< ImagePtrT, FootprintPtrT > buildSymmetricTemplate(MaskedImageT const &img, lsst::afw::detection::Footprint const &foot, lsst::afw::detection::PeakRecord const &pk, double sigma1, bool minZero, bool patchEdges, bool *patchedEdges)
Given an img, footprint foot, and peak, creates a symmetric template around the peak; produce a Maske...
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
#define LOG_GET(logger)
Returns a Log object associated with logger.
Definition: Log.h:75
RelativeSpanIterator operator++()
RelativeSpanIterator operator++(int dummy)
lsst::geom::Box2I getBBox(ImageOrigin const origin=PARENT) const
Definition: MaskedImage.h:1097
Record class that represents a peak in a Footprint.
Definition: Peak.h:42
_view_t::xy_locator xy_locator
An xy_locator.
Definition: ImageBase.h:121
boost::shared_ptr< lsst::afw::image::MaskedImage< ImagePixelT > MaskPixelT, VariancePixelT > MaskedImagePtrT
Definition: BaselineUtils.h:26
xy_locator xy_at(int x, int y) const
Return an xy_locator at the point (x, y) in the image.
Definition: ImageBase.h:462
PeakCatalog & getPeaks()
Return the Peaks contained in this Footprint.
Definition: Footprint.h:129
An integer coordinate rectangle.
Definition: Box.h:55
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:58
py::object result
Definition: _schema.cc:429
#define PTR(...)
Definition: base.h:41
T real(const T &x)
Definition: Integrate.h:199
bool operator>(const SpanSet::const_iterator &other)
int end
bool operator<=(const SpanSet::const_iterator &other)
T lround(T... args)
int getMinY() const noexcept
Definition: Box.h:158
boost::shared_ptr< lsst::afw::detection::HeavyFootprint< ImagePixelT > MaskPixelT, VariancePixelT > HeavyFootprintPtrT
Definition: BaselineUtils.h:36
int getX0() const noexcept
Return the starting x-value.
Definition: Span.h:72
table::Key< double > sigma1
boost::shared_ptr< lsst::afw::image::Image< ImagePixelT > > ImagePtrT
Definition: BaselineUtils.h:28
Reports errors that are due to events beyond the control of the program.
Definition: Runtime.h:104
A const locator for the MaskedImage.
Definition: MaskedImage.h:108