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