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