LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
LSSTDataManagementBasePackage
Footprint.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2008-2015 LSST Corporation.
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <http://www.lsstcorp.org/LegalNotices/>.
21  */
22 
23 /*****************************************************************************/
28 #include <cstdint>
29 #include <cassert>
30 #include <string>
31 #include <typeinfo>
32 #include <algorithm>
33 #include <cmath>
34 #include "boost/format.hpp"
35 #include "lsst/pex/exceptions.h"
36 #include "lsst/afw/image/Mask.h"
37 #include "lsst/afw/math/Kernel.h"
42 #include "lsst/afw/geom/Point.h"
47 
48 namespace lsst {
49 namespace afw {
50 namespace detection {
51 
52 // anonymous namespace
53 namespace {
54 
55 /*
56  * Compare two Span%s by y, then x0, then x1
57  *
58  * A utility functor passed to sort; needed to dereference the std::shared_ptrs.
59  */
60  struct compareSpanByYX :
61  public std::binary_function<Span::ConstPtr, Span::ConstPtr, bool> {
62  int operator()(Span::ConstPtr a, Span::ConstPtr b) {
63  return (*a) < (*b);
64  }
65  };
66 
68 geom::Point2D transformPoint(double x, double y,
69  image::Wcs const& source,
70  image::Wcs const& target){
71  return target.skyToPixel(*source.pixelToSky(x, y));
72 }
73 
74 
75 } //end namespace
76 
77 /*****************************************************************************/
79 int Footprint::id = 0;
80 
82  int nspan,
83  geom::Box2I const & region
84 ) : lsst::daf::base::Citizen(typeid(this)),
85  _fid(++id),
86  _area(0),
87  _bbox(geom::Box2I()),
88  _peaks(PeakTable::makeMinimalSchema()),
89  _region(region),
90  _normalized(true)
91 {
92  if (nspan < 0) {
93  throw LSST_EXCEPT(
94  lsst::pex::exceptions::InvalidParameterError,
95  str(boost::format("Number of spans requested is -ve: %d") % nspan));
96  }
97 }
98 
105  afw::table::Schema const & peakSchema,
106  int nspan,
107  geom::Box2I const & region
108 ) : lsst::daf::base::Citizen(typeid(this)),
109  _fid(++id),
110  _area(0),
111  _bbox(geom::Box2I()),
112  _peaks(peakSchema),
113  _region(region),
114  _normalized(true)
115 {
116  if (nspan < 0) {
117  throw LSST_EXCEPT(
118  lsst::pex::exceptions::InvalidParameterError,
119  str(boost::format("Number of spans requested is -ve: %d") % nspan));
120  }
121 }
126  geom::Box2I const& bbox,
127  geom::Box2I const& region
128 ) : lsst::daf::base::Citizen(typeid(this)),
129  _fid(++id),
130  _area(0),
131  _bbox(bbox),
132  _peaks(PeakTable::makeMinimalSchema()),
133  _region(region)
134 {
135  int const x0 = bbox.getMinX();
136  int const y0 = bbox.getMinY();
137  int const x1 = bbox.getMaxX();
138  int const y1 = bbox.getMaxY();
139 
140  for (int i = y0; i <= y1; ++i) {
141  addSpan(i, x0, x1);
142  }
143  _normalized=true;
144 }
145 
147  geom::Point2I const & center,
148  double const radius,
149  geom::BoxI const & region
150 ) : lsst::daf::base::Citizen(typeid(this)),
151  _fid(++id),
152  _area(0),
153  _bbox(geom::BoxI()),
154  _peaks(PeakTable::makeMinimalSchema()),
155  _region(region)
156 {
157  int const r2 = static_cast<int>(radius*radius + 0.5); // rounded radius^2
158  int const r = static_cast<int>(std::sqrt(static_cast<double>(r2))); // truncated radius; r*r <= r2
159 
160  for (int i = -r; i <= r; ++i) {
161  int hlen = static_cast<int>(std::sqrt(static_cast<double>(r2 - i*i)));
162  addSpan(center.getY() + i, center.getX() - hlen, center.getX() + hlen);
163  }
164  _normalized = true;
165 }
166 
168  geom::ellipses::Ellipse const & ellipse,
169  geom::Box2I const & region
170 ) : lsst::daf::base::Citizen(typeid(this)),
171  _fid(++id),
172  _area(0),
173  _bbox(geom::Box2I()),
174  _peaks(PeakTable::makeMinimalSchema()),
175  _region(region),
176  _normalized(true)
177 {
178  geom::ellipses::PixelRegion pr(ellipse);
179  for (
180  geom::ellipses::PixelRegion::Iterator spanIter = pr.begin(), end = pr.end();
181  spanIter != end;
182  ++spanIter
183  ) {
184  if (!spanIter->isEmpty()) {
185  addSpan(*spanIter);
186  }
187  }
188  _normalized = true;
189 }
190 
192  Footprint::SpanList const & spans,
193  geom::Box2I const & region
194 ) : lsst::daf::base::Citizen(typeid(this)),
195  _fid(++id),
196  _area(0),
197  _bbox(geom::Box2I()),
198  _peaks(PeakTable::makeMinimalSchema()),
199  _region(region),
200  _normalized(false)
201 {
202  _spans.reserve(spans.size());
203  for(SpanList::const_iterator i(spans.begin()); i != spans.end(); ++i) {
204  addSpan(**i);
205  }
206 }
207 
208 Footprint::Footprint(Footprint const & other)
209  : lsst::daf::base::Citizen(typeid(this)),
210  _fid(++id),
211  _bbox(other._bbox),
212  // peaks are deep-copied, but use the same Table as other
213  _peaks(other.getPeaks().getTable(), other.getPeaks().begin(), other.getPeaks().end(), true),
214  _region(other._region)
215 {
216  //deep copy spans
217  _spans.reserve(other._spans.size());
218  for(SpanList::const_iterator i(other._spans.begin());
219  i != other._spans.end(); ++i
220  ) {
221  addSpan(**i);
222  }
223  _area = other._area;
224  _normalized = other._normalized;
225 
226 }
227 
228 Footprint::~Footprint() {
229 }
230 
231 
232 PTR(PeakRecord) Footprint::addPeak(float fx, float fy, float value) {
233  PTR(PeakRecord) p = getPeaks().addNew();
234  p->setIx(fx);
235  p->setIy(fy);
236  p->setFx(fx);
237  p->setFy(fy);
238  p->setPeakValue(value);
239  return p;
240 }
241 
242 namespace {
243 
244 // comparison function to sort peaks from most positive to most negative.
245 struct SortPeaks {
246 
247  SortPeaks(afw::table::Key<float> const & key) : _key(key) {}
248 
249  bool operator()(detection::PeakRecord const & a, detection::PeakRecord const & b) const {
250  return a.get(_key) > b.get(_key);
251  }
252 
253 private:
254  afw::table::Key<float> _key;
255 };
256 
257 } // anonymous
258 
259 void Footprint::sortPeaks(afw::table::Key<float> const & key) {
260  getPeaks().sort(SortPeaks(key.isValid() ? key : PeakTable::getPeakValueKey()));
261 }
262 
266 bool Footprint::contains(
267  lsst::afw::geom::Point2I const& pix
268 ) const
269 {
270  if (_bbox.contains(pix)) {
271  for (Footprint::SpanList::const_iterator siter = _spans.begin(); siter != _spans.end(); ++siter) {
272  if ((*siter)->contains(pix.getX(), pix.getY())) {
273  return true;
274  }
275  }
276  }
277 
278  return false;
279 }
280 
281 /******************************************************************************/
282 /*
283  * Return the bitwise OR of all the mask bits of all the mask pixels that fall in the Footprint
284  */
285 template<typename MaskT>
286 MaskT Footprint::overlapsMask(image::Mask<MaskT> const& mask
287  ) const
288 {
289  int const width = static_cast<int>(mask.getWidth());
290  int const height = static_cast<int>(mask.getHeight());
291 
292  MaskT bitmask = 0;
293  for (Footprint::SpanList::const_iterator siter =
294  getSpans().begin(); siter != getSpans().end(); siter++) {
295  PTR(Span) const span = *siter;
296  int const y = span->getY() - mask.getY0();
297  if (y < 0 || y >= height) {
298  continue;
299  }
300 
301  int x0 = span->getX0() - mask.getX0();
302  int x1 = span->getX1() - mask.getX0();
303  x0 = (x0 < 0) ? 0 : (x0 >= width ? width - 1 : x0);
304  x1 = (x1 < 0) ? 0 : (x1 >= width ? width - 1 : x1);
305 
306  for (typename image::Image<MaskT>::x_iterator ptr = mask.x_at(x0, y),
307  end = mask.x_at(x1 + 1, y); ptr != end; ++ptr) {
308  bitmask |= *ptr;
309  }
310  }
311 
312  return bitmask;
313 }
314 
315 namespace {
316 
318 struct ClipSpansPredicate : public std::unary_function<PTR(Span) const&, bool> {
319  geom::Box2I const& bbox;
320 
321  ClipSpansPredicate(geom::Box2I const& _bbox) : bbox(_bbox) {}
322 
323  bool operator()(PTR(Span) const& spanPtr) const {
324  Span const& span = *spanPtr;
325  int const y = span.getY();
326  return (y < bbox.getMinY() || y > bbox.getMaxY() ||
327  span.getX0() > bbox.getMaxX() || span.getX1() < bbox.getMinX());
328  }
329 };
330 
332 struct ClipPeaksPredicate : public std::unary_function<PeakRecord const&, bool> {
333  geom::Box2I const& bbox;
334 
335  ClipPeaksPredicate(geom::Box2I const& _bbox) : bbox(_bbox) {}
336 
337  bool operator()(PTR(PeakRecord) const& peak) const {
338  return !bbox.contains(geom::Point2I(peak->getIx(), peak->getIy()));
339  }
340 };
341 } // anonymous namespace
342 
343 void Footprint::clipTo(geom::Box2I const& bbox) {
344  _spans.erase(std::remove_if(_spans.begin(), _spans.end(), ClipSpansPredicate(bbox)), _spans.end());
345  for (SpanList::const_iterator ss = _spans.begin(); ss != _spans.end(); ++ss) {
346  Span& span = **ss;
347  span.getX0() = std::max(span.getX0(), bbox.getMinX());
348  span.getX1() = std::min(span.getX1(), bbox.getMaxX());
349  }
350 
351  // Remove peaks not in the new bbox
352  _peaks.getInternal().erase(std::remove_if(_peaks.getInternal().begin(), _peaks.getInternal().end(),
353  ClipPeaksPredicate(bbox)), _peaks.getInternal().end());
354 
355  if (_spans.empty()) {
356  _bbox = geom::Box2I();
357  _area = 0;
358  _normalized = true;
359  } else {
360  _normalized = false;
361  normalize();
362  }
363 }
364 
365 void Footprint::normalize() {
366  if (_normalized) {
367  return;
368  }
369  if (_spans.empty()) {
370  _bbox = geom::Box2I();
371  _normalized = true;
372  _area = 0;
373  return;
374  }
375  //
376  // Check that the spans are sorted, and (more importantly) that each pixel appears
377  // in only one span
378  //
379  sort(_spans.begin(), _spans.end(), compareSpanByYX());
380 
381  Footprint::SpanList::iterator ptr = _spans.begin(), end = _spans.end();
382  Span *lspan = ptr->get(); // Left span
383  int y = lspan->_y;
384  int x1 = lspan->_x1;
385  _area = lspan->getWidth();
386  int minX = lspan->_x0, minY=y, maxX=x1;
387 
388  ++ptr;
389 
390  for (; ptr != end; ++ptr) {
391  Span *rspan = ptr->get(); // Right span
392  if (rspan->_y == y) {
393  if (rspan->_x0 <= x1 + 1) { // Spans overlap or touch
394  if (rspan->_x1 > x1) { // right span extends left span
395  //update area
396  _area += rspan->_x1 - x1;
397  //update end of current span
398  x1 = lspan->_x1 = rspan->_x1;
399  //update bounds
400  if(x1 > maxX) maxX = x1;
401  }
402 
403  ptr = _spans.erase(ptr);
404  end = _spans.end(); // delete the right span
405  if (ptr == end) {
406  break;
407  }
408 
409  --ptr;
410  continue;
411  }
412  else{
413  _area += rspan->getWidth();
414  if(rspan->_x1 > maxX) maxX = rspan->_x1;
415  }
416  } else {
417  _area += rspan->getWidth();
418  }
419 
420  y = rspan->_y;
421  x1 = rspan->_x1;
422 
423  lspan = rspan;
424  if(lspan->_x0 < minX) minX = lspan->_x0;
425  if(x1 > maxX) maxX = x1;
426  }
427  _bbox = geom::Box2I(geom::Point2I(minX, minY), geom::Point2I(maxX, y));
428 
429  _normalized = true;
430 }
431 
432 Span const& Footprint::addSpan(
433  int const y,
434  int const x0,
435  int const x1
436 ) {
437  if (x1 < x0) {
438  return addSpan(y, x1, x0);
439  }
440 
441  PTR(Span) sp(new Span(y, x0, x1));
442  _spans.push_back(sp);
443 
444  _area += sp->getWidth();
445  _normalized = false;
446 
447  _bbox.include(geom::Point2I(x0, y));
448  _bbox.include(geom::Point2I(x1, y));
449 
450  return *sp.get();
451 }
452 
453 const Span& Footprint::addSpan(
454  Span const& span
455 ) {
456  return addSpan(span._y, span._x0, span._x1);
457 }
458 
459 const Span& Footprint::addSpan(
460  Span const& span,
461  int dx,
462  int dy
463 ) {
464  return addSpan(span._y + dy, span._x0 + dx, span._x1 + dx);
465 }
466 
467 const Span& Footprint::addSpanInSeries(
468  int const y,
469  int const x0,
470  int const x1
471 ) {
472  if (x1 < x0) {
473  return addSpanInSeries(y, x1, x0);
474  }
475  if (_spans.size() == 0) {
476  const Span& s = addSpan(y, x0, x1);
477  _normalized = true;
478  return s;
479  }
480  // merge contiguous spans
481  PTR(Span) lastspan = _spans.back();
482  if ((y == lastspan->getY()) &&
483  (x0 == (lastspan->getX1() + 1))) {
484  // contiguous.
485  lastspan->_x1 = x1;
486  _area += (1 + x1 - x0);
487  _bbox.include(geom::Point2I(x1,y));
488  return *lastspan;
489  }
490  if (!((y > lastspan->getY()) ||
491  (x0 > (lastspan->getX1() + 1)))) {
492  throw LSST_EXCEPT(
493  lsst::pex::exceptions::InvalidParameterError,
494  str(boost::format("addSpanInSeries: new span %i,[%i,%i] is NOT in series after last span "
495  "%i,[%i,%i]") %
496  y % x0 % x1 % lastspan->getY() % lastspan->getX0() % lastspan->getX1()));
497  }
498  const Span& s = addSpan(y, x0, x1);
499  _normalized = true;
500  return s;
501 }
502 
503 void Footprint::shift(
504  int dx,
505  int dy
506 ) {
507  for (SpanList::iterator i = _spans.begin(); i != _spans.end(); ++i){
508  PTR(Span) span = *i;
509 
510  span->_y += dy;
511  span->_x0 += dx;
512  span->_x1 += dx;
513  }
514 
515  _bbox.shift(geom::Extent2I(dx, dy));
516 }
517 
518 geom::Point2D
519 Footprint::getCentroid() const
520 {
521  int n = 0;
522  double xc = 0, yc = 0;
523  for (Footprint::SpanList::const_iterator siter = _spans.begin(); siter != _spans.end(); ++siter) {
524  CONST_PTR(Span) span = *siter;
525  int const y = span->getY();
526  int const x0 = span->getX0();
527  int const x1 = span->getX1();
528  int const npix = x1 - x0 + 1;
529 
530  n += npix;
531  xc += npix*0.5*(x1 + x0);
532  yc += npix*y;
533  }
534  assert(n == _area);
535 
536  return geom::Point2D(xc/_area, yc/_area);
537 }
538 
539 geom::ellipses::Quadrupole
540 Footprint::getShape() const
541 {
542  geom::Point2D cen = getCentroid();
543  double const xc = cen.getX();
544  double const yc = cen.getY();
545 
546  double sumxx = 0, sumxy = 0, sumyy = 0;
547  for (Footprint::SpanList::const_iterator siter = _spans.begin(); siter != _spans.end(); ++siter) {
548  CONST_PTR(Span) span = *siter;
549  int const y = span->getY();
550  int const x0 = span->getX0();
551  int const x1 = span->getX1();
552  int const npix = x1 - x0 + 1;
553 
554  for (int x = x0; x <= x1; ++x) {
555  sumxx += (x - xc)*(x - xc);
556  }
557  sumxy += npix*(0.5*(x1 + x0) - xc)*(y - yc);
558  sumyy += npix*(y - yc)*(y - yc);
559  }
560 
561  return geom::ellipses::Quadrupole(sumxx/_area, sumyy/_area, sumxy/_area);
562 }
563 
564 namespace {
565  /*
566  * Set the pixels in idImage which are in Footprint by adding or
567  * replacing the specified value to the Image
568  *
569  * The ids that are overwritten are returned for the callers deliction
570  */
571  template<bool overwriteId, typename PixelT>
572  void
573  doInsertIntoImage(geom::Box2I const& _region, // unpacked from Footprint
574  Footprint::SpanList const& _spans, // unpacked from Footprint
575  image::Image<PixelT>& idImage, // Image to contain the footprint
576  std::uint64_t const id, // Add/replace id to idImage for pixels in Footprint
577  geom::Box2I const& region, // Footprint's region (default: getRegion())
578  long const mask=0x0, // Don't overwrite bits in this mask
579  std::set<std::uint64_t> *oldIds=NULL // if non-NULL, set the IDs that were overwritten
580  )
581  {
582  int width, height, x0, y0;
583  if(!region.isEmpty()) {
584  height = region.getHeight();
585  width = region.getWidth();
586  x0 = region.getMinX();
587  y0 = region.getMinY();
588  } else {
589  height = _region.getHeight();
590  width = _region.getWidth();
591  x0 = _region.getMinX();
592  y0 = _region.getMinY();
593  }
594 
595  if (width != idImage.getWidth() || height != idImage.getHeight()) {
596  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
597  str(boost::format("Image of size (%dx%d) doesn't match "
598  "Footprint's host Image of size (%dx%d)") %
599  idImage.getWidth() % idImage.getHeight() % width % height));
600  }
601 
602  if (id & mask) {
603  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
604  str(boost::format("Id 0x%x sets bits in the protected mask 0x%x") % id % mask));
605  }
606 
607  typename std::set<std::uint64_t>::const_iterator pos; // hint on where to insert into oldIds
608  if (oldIds) {
609  pos = oldIds->begin();
610  }
611  for (Footprint::SpanList::const_iterator spi = _spans.begin(); spi != _spans.end(); ++spi) {
612  CONST_PTR(Span) span = *spi;
613 
614  int const sy0 = span->getY() - y0;
615  if (sy0 < 0 || sy0 >= height) {
616  continue;
617  }
618 
619  int sx0 = span->getX0() - x0;
620  if (sx0 < 0) {
621  sx0 = 0;
622  }
623  int sx1 = span->getX1() - x0;
624  int const swidth = (sx1 >= width) ? width - sx0 : sx1 - sx0 + 1;
625 
626  for (typename image::Image<PixelT>::x_iterator ptr = idImage.x_at(sx0, sy0),
627  end = ptr + swidth; ptr != end; ++ptr) {
628  if (overwriteId) {
629  long val = *ptr & ~mask;
630  if (val != 0 and oldIds != NULL) {
631  pos = oldIds->insert(pos, val); // update our hint, pos
632  }
633  *ptr = (*ptr & mask) + id;
634  } else {
635  *ptr += id;
636  }
637  }
638  }
639  }
640 }
641 
642 template<typename PixelT>
643 void
644 Footprint::clipToNonzero(typename image::Image<PixelT> const& img) {
645  typedef lsst::afw::image::Image<PixelT> ImageT;
646  int const ix0 = img.getX0();
647  int const iy0 = img.getY0();
648  PixelT const zero = 0;
649 
650  normalize(); // allows us to produce a normalized output
651  SpanList old;
652  std::swap(_spans, old);
653  _spans.reserve(old.size());
654  _area = 0;
655  _bbox = geom::Box2I();
656  for (SpanList::iterator s = old.begin(); s != old.end(); ++s) {
657  int const y = (*s)->getY();
658  int const x0 = (*s)->getX0();
659  int const x1 = (*s)->getX1();
660  typename ImageT::x_iterator img_it = img.row_begin(y - iy0) + (x0 - ix0);
661  int leftx, rightx;
662  // find zero pixels on the left...
663  for (leftx = x0; leftx <= x1; ++leftx, ++img_it) {
664  if (*img_it != zero) {
665  break;
666  }
667  }
668  if (leftx > x1) {
669  // whole span is zero; drop it.
670  continue;
671  }
672  // find zero pixels on the right...
673  img_it = img.row_begin(y - iy0) + (x1 - ix0);
674  for (rightx = x1; rightx >= leftx; --rightx, --img_it) {
675  if (*img_it != zero) {
676  break;
677  }
678  }
679  addSpanInSeries(y, leftx, rightx);
680  }
681  normalize();
682 }
683 
684 template<typename PixelT>
685 void
686 Footprint::insertIntoImage(
687  typename image::Image<PixelT>& idImage,
688  std::uint64_t const id,
689  geom::Box2I const& region
690 ) const
691 {
692  static_cast<void>(doInsertIntoImage<false>(_region, _spans, idImage, id, region));
693 }
694 
695 template<typename PixelT>
696 void
697 Footprint::insertIntoImage(
698  image::Image<PixelT>& idImage,
699  std::uint64_t const id,
700  bool overwriteId,
701  long const mask,
702  std::set<std::uint64_t> *oldIds,
703  geom::Box2I const& region
704 ) const
705 {
706  if (id > std::size_t(std::numeric_limits<PixelT>::max())) {
707  throw LSST_EXCEPT(
708  lsst::pex::exceptions::OutOfRangeError,
709  "id out of range for image type"
710  );
711  }
712  if (overwriteId) {
713  doInsertIntoImage<true>(_region, _spans, idImage, id, region, mask, oldIds);
714  } else {
715  doInsertIntoImage<false>(_region, _spans, idImage, id, region, mask, oldIds);
716  }
717 }
718 
719 void Footprint::include(std::vector<PTR(Footprint)> const & others, bool ignoreSelf) {
720  if (others.empty()) return;
721  geom::Box2I bbox;
722  if (!ignoreSelf) {
723  bbox.include(getBBox());
724  } else {
725  _spans.clear();
726  }
727  for (std::vector<PTR(Footprint)>::const_iterator i = others.begin(); i != others.end(); ++i) {
728  bbox.include((**i).getBBox());
729  }
730  std::uint16_t bits = 0x1;
731  image::Mask<std::uint16_t> mask(bbox);
732  if (!ignoreSelf) {
733  setMaskFromFootprint(&mask, *this, bits);
734  }
735  for (std::vector<PTR(Footprint)>::const_iterator i = others.begin(); i != others.end(); ++i) {
736  setMaskFromFootprint(&mask, **i, bits);
737  }
738  FootprintSet fpSet(mask, Threshold(bits, Threshold::BITMASK));
739  if (fpSet.getFootprints()->empty()) {
740  _spans.clear();
741  } else if (fpSet.getFootprints()->size() == 1u) {
742  _spans.swap(fpSet.getFootprints()->front()->getSpans());
743  } else {
744  _spans.clear();
745  for (std::vector<PTR(Footprint)>::const_iterator i = fpSet.getFootprints()->begin();
746  i != fpSet.getFootprints()->end(); ++i) {
747  _spans.insert(_spans.end(), (**i).getSpans().begin(), (**i).getSpans().end());
748  }
749  }
750  _normalized = false;
751  normalize();
752 }
753 
754 // Factory class used for table-based persistence; invoked via registry in afw::table::io
755 class FootprintFactory : public table::io::PersistableFactory {
756 public:
757 
758  virtual PTR(table::io::Persistable)
759  read(InputArchive const & archive, CatalogVector const & catalogs) const {
760  LSST_ARCHIVE_ASSERT(catalogs.size() == 2u);
761  PTR(Footprint) result = std::make_shared<Footprint>();
762  result->readSpans(catalogs.front());
763  result->readPeaks(catalogs.back());
764  return result;
765  }
766 
767  explicit FootprintFactory(std::string const & name) : table::io::PersistableFactory(name) {}
768 
769 };
770 
771 namespace {
772 
773 // Singleton helper class that manages the schema and keys for persisting a Footprint
774 class FootprintPersistenceHelper {
775 public:
776  table::Schema spanSchema;
777  table::Key<int> spanY;
778  table::Key<int> spanX0;
779  table::Key<int> spanX1;
780 
781  static FootprintPersistenceHelper const & get() {
782  static FootprintPersistenceHelper instance;
783  return instance;
784  }
785 
786  // No copying
787  FootprintPersistenceHelper (const FootprintPersistenceHelper&) = delete;
788  FootprintPersistenceHelper& operator=(const FootprintPersistenceHelper&) = delete;
789 
790  // No moving
791  FootprintPersistenceHelper (FootprintPersistenceHelper&&) = delete;
792  FootprintPersistenceHelper& operator=(FootprintPersistenceHelper&&) = delete;
793 
794 private:
795  FootprintPersistenceHelper() :
796  spanSchema(),
797  spanY(spanSchema.addField<int>("y", "row position of span", "pixel")),
798  spanX0(spanSchema.addField<int>("x0", "first column of span (inclusive)", "pixel")),
799  spanX1(spanSchema.addField<int>("x1", "first column of span (inclusive)", "pixel"))
800  {
801  spanSchema.getCitizen().markPersistent();
802  }
803 };
804 
805 std::string getFootprintPersistenceName() { return "Footprint"; }
806 
807 // Insert the factory into the registry (instantiating an instance is sufficient, because
808 // the code that does the work is in the base class ctor)
809 FootprintFactory registration(getFootprintPersistenceName());
810 
811 } // anonymous
812 
813 std::string Footprint::getPersistenceName() const { return getFootprintPersistenceName(); }
814 
815 std::string Footprint::getPythonModule() const { return "lsst.afw.detection"; }
816 
817 void Footprint::write(OutputArchiveHandle & handle) const {
818  FootprintPersistenceHelper const & keys = FootprintPersistenceHelper::get();
819  afw::table::BaseCatalog spanCat = handle.makeCatalog(keys.spanSchema);
820  spanCat.reserve(_spans.size());
821  for (SpanList::const_iterator i = _spans.begin(); i != _spans.end(); ++i) {
822  PTR(afw::table::BaseRecord) record = spanCat.addNew();
823  record->set(keys.spanY, (**i).getY());
824  record->set(keys.spanX0, (**i).getX0());
825  record->set(keys.spanX1, (**i).getX1());
826  }
827  handle.saveCatalog(spanCat);
828  afw::table::BaseCatalog peakCat = handle.makeCatalog(_peaks.getSchema());
829  peakCat.insert(peakCat.end(), _peaks.begin(), _peaks.end(), true);
830  handle.saveCatalog(peakCat);
831 }
832 
833 void Footprint::readSpans(afw::table::BaseCatalog const & spanCat) {
834  FootprintPersistenceHelper const & keys = FootprintPersistenceHelper::get();
835  for (afw::table::BaseCatalog::const_iterator i = spanCat.begin(); i != spanCat.end(); ++i) {
836  addSpan(i->get(keys.spanY), i->get(keys.spanX0), i->get(keys.spanX1));
837  }
838 }
839 
840 void Footprint::readPeaks(afw::table::BaseCatalog const & peakCat) {
841  if (!peakCat.getSchema().contains(PeakTable::makeMinimalSchema())) {
842  // need to handle an older form of Peak persistence for backwards compatibility
843  afw::table::SchemaMapper mapper(peakCat.getSchema());
844  mapper.addMinimalSchema(PeakTable::makeMinimalSchema());
845  afw::table::Key<float> oldX = peakCat.getSchema()["x"];
846  afw::table::Key<float> oldY = peakCat.getSchema()["y"];
847  afw::table::Key<float> oldPeakValue = peakCat.getSchema()["value"];
848  mapper.addMapping(oldX, "f.x");
849  mapper.addMapping(oldY, "f.y");
850  mapper.addMapping(oldPeakValue, "peakValue");
851  _peaks = PeakCatalog(mapper.getOutputSchema());
852  _peaks.reserve(peakCat.size());
853  for (afw::table::BaseCatalog::const_iterator i = peakCat.begin(); i != peakCat.end(); ++i) {
854  PTR(PeakRecord) newPeak = _peaks.addNew();
855  newPeak->assign(*i, mapper);
856  newPeak->setIx(int(newPeak->getFx()));
857  newPeak->setIy(int(newPeak->getFy()));
858  }
859  return;
860  }
861  _peaks = PeakCatalog(peakCat.getSchema());
862  _peaks.reserve(peakCat.size());
863  for (afw::table::BaseCatalog::const_iterator i = peakCat.begin(); i != peakCat.end(); ++i) {
864  _peaks.addNew()->assign(*i);
865  }
866 }
867 
871 Footprint & Footprint::operator=(Footprint & other) {
872  _region = other._region;
873 
874  //deep copy spans
875  _spans = SpanList();
876  _spans.reserve(other._spans.size());
877  for(SpanList::const_iterator i(other._spans.begin());
878  i != other._spans.end(); ++i
879  ) {
880  addSpan(**i);
881  }
882  _area = other._area;
883  _normalized = other._normalized;
884  _bbox = other._bbox;
885 
886  //deep copy peaks
887  _peaks = PeakCatalog(other.getPeaks().getTable(), other.getPeaks().begin(), other.getPeaks().end(), true);
888  return *this;
889 }
890 
891 template<typename MaskT>
892 void Footprint::intersectMask(
893  lsst::afw::image::Mask<MaskT> const & mask,
894  MaskT const bitmask
895 ) {
896  geom::Box2I maskBBox = mask.getBBox();
897 
898  //this operation makes no sense on non-normalized footprints.
899  //make sure this is normalized
900  normalize();
901 
902  SpanList::iterator s(_spans.begin());
903  while((*s)->getY() < maskBBox.getMinY() && s != _spans.end()){
904  ++s;
905  }
906 
907 
908  int x0, x1, y;
909  SpanList maskedSpans;
910  int maskedArea=0;
911  for( ; s != _spans.end(); ++s) {
912  y = (*s)->getY();
913 
914  if (y > maskBBox.getMaxY())
915  break;
916 
917  x0 = (*s)->getX0();
918  x1 = (*s)->getX1();
919 
920  if(x1 < maskBBox.getMinX() || x0 > maskBBox.getMaxX()) {
921  //span is entirely outside the image mask. cannot be used
922  continue;
923  }
924 
925  //clip the span to be within the mask
926  if(x0 < maskBBox.getMinX()) x0 = maskBBox.getMinX();
927  if(x1 > maskBBox.getMaxX()) x1 = maskBBox.getMaxX();
928 
929  //Image iterators are always specified with respect to (0,0)
930  //regardless what the image::XY0 is set to.
931  typename image::Mask<MaskT>::const_x_iterator mIter = mask.x_at(
932  x0 - maskBBox.getMinX(), y - maskBBox.getMinY()
933  );
934 
935  //loop over all span locations, slicing the span at maskedPixels
936  for(int x = x0; x <= x1; ++x, ++mIter) {
937  if((*mIter & bitmask) != 0) {
938  //masked pixel found within span
939  if (x > x0) {
940  //add beginning of span to the output
941  //the fixed span contains all the unmasked pixels up to,
942  //but not including this masked pixel
943  PTR(Span) maskedSpan(new Span(y, x0, x- 1));
944  maskedSpans.push_back(maskedSpan);
945  maskedArea += maskedSpan->getWidth();
946  }
947  //set the next Span to start after this pixel
948  x0 = x + 1;
949  }
950  }
951 
952  //add last section of span
953  if(x0 <= x1) {
954  PTR(Span) maskedSpan(new Span(y, x0, x1));
955  maskedSpans.push_back(maskedSpan);
956  maskedArea += maskedSpan->getWidth();
957  }
958  }
959  _area = maskedArea;
960  _spans = maskedSpans;
961  _bbox.clip(maskBBox);
962 }
963 
964 
965 PTR(Footprint) Footprint::transform(
966  image::Wcs const& source,
967  image::Wcs const& target,
968  geom::Box2I const& region,
969  bool doClip
970 ) const {
971  // Transform the original bounding box
972  geom::Box2I const& fpBox = getBBox(); // Original bounding box
973  geom::Box2D tBoxD;
974  // If slow, could consider linearising the WCSes and combining the
975  // linear versions to a single transform, and then using that to
976  // transform all the points.
977  tBoxD.include(transformPoint(fpBox.getMinX(), fpBox.getMinY(), source, target));
978  tBoxD.include(transformPoint(fpBox.getMinX(), fpBox.getMaxY(), source, target));
979  tBoxD.include(transformPoint(fpBox.getMaxX(), fpBox.getMinY(), source, target));
980  tBoxD.include(transformPoint(fpBox.getMaxX(), fpBox.getMaxY(), source, target));
981  geom::Box2I tBoxI(tBoxD);
982 
983  // enumerate points in the new bbox that, when reverse-transformed, are within the given footprint.
984  PTR(Footprint) fpNew = std::make_shared<Footprint>(getPeaks().getSchema(), 0, region);
985 
986  for (int y = tBoxI.getBeginY(); y < tBoxI.getEndY(); ++y) {
987  bool inSpan = false; // Are we in a span?
988  int start = -1; // Start of span
989 
990  for (int x = tBoxI.getBeginX(); x < tBoxI.getEndX(); ++x) {
991  geom::Point2D p = transformPoint(x, y, target, source);
992  int const xSource = std::floor(0.5 + p.getX());
993  int const ySource = std::floor(0.5 + p.getY());
994 
995  if (contains(geom::Point2I(xSource, ySource))) {
996  if (!inSpan) {
997  inSpan = true;
998  start = x;
999  }
1000  } else if (inSpan) {
1001  inSpan = false;
1002  fpNew->addSpan(y, start, x - 1);
1003  }
1004  }
1005  if (inSpan) {
1006  fpNew->addSpan(y, start, tBoxI.getMaxX());
1007  }
1008  }
1009 
1010  // Copy over peaks to new Footprint
1011  for (
1012  PeakCatalog::const_iterator iter = this->getPeaks().begin();
1013  iter != this->getPeaks().end();
1014  ++iter
1015  ) {
1016  geom::Point2D tp = transformPoint(iter->getFx(), iter->getFy(), source, target);
1017  fpNew->addPeak(tp.getX(), tp.getY(), iter->getPeakValue());
1018  }
1019 
1020  if (doClip) {
1021  fpNew->clipTo(region);
1022  }
1023  return fpNew;
1024 }
1025 
1026 PTR(Footprint) Footprint::findEdgePixels() const
1027 {
1028  if (!_normalized) {
1029  throw LSST_EXCEPT(pex::exceptions::InvalidParameterError, "Footprint isn't normalized");
1030  }
1031  int const width = getBBox().getWidth(), height = getBBox().getHeight();
1032  if (height <= 2 || _spans.size() <= 2) {
1033  // Everything is on the edge
1034  return std::make_shared<Footprint>(*this);
1035  }
1036 
1037  // Get a list of pixels (in the form of a Footprint) that are on the edge horizontally
1038  // or have nothing above or below them.
1039  PTR(Footprint) edges = std::make_shared<Footprint>(getPeaks().getSchema());
1040  int const xStart = getBBox().getMinX(), yStart = getBBox().getMinY();
1041  std::vector<bool> rowBefore(width, false); // Representation of the previous row
1042  std::vector<bool> rowNow(width, false); // Representation of this row
1043  std::vector<bool> rowAfter(width, false); // Representation of the next row
1044 
1045  int yLast = yStart; // y value of last span we looked at
1046  int const yEnd = _spans.back()->getY(); // y value of end span
1047 
1048  // Set rowNow, rowAfter
1049  SpanList::const_iterator readAhead = _spans.begin(); // Iterator for loading next row
1050  for (; readAhead != _spans.end() && (*readAhead)->getY() == yStart; ++readAhead) {
1051  std::fill(rowNow.begin() + (*readAhead)->getX0() - xStart,
1052  rowNow.begin() + (*readAhead)->getX1() + 1 - xStart,
1053  true);
1054  }
1055  for (; readAhead != _spans.end() && (*readAhead)->getY() == yStart + 1; ++readAhead) {
1056  std::fill(rowAfter.begin() + (*readAhead)->getX0() - xStart,
1057  rowAfter.begin() + (*readAhead)->getX1() + 1 - xStart,
1058  true);
1059  }
1060 
1061  for (SpanList::const_iterator ss = _spans.begin(); ss != _spans.end(); ++ss) {
1062  int const y = (*ss)->getY();
1063  if (y == yStart || y == yEnd) {
1064  // The whole span is on an edge
1065  edges->addSpanInSeries(y, (*ss)->getX0(), (*ss)->getX1());
1066  continue;
1067  }
1068  if (y != yLast) {
1069  // Move rows down
1070  rowBefore.assign(rowNow.begin(), rowNow.end());
1071  rowNow.assign(rowAfter.begin(), rowAfter.end());
1072  // Prepare the next row
1073  std::fill(rowAfter.begin(), rowAfter.end(), false);
1074  for (; readAhead != _spans.end() && (*readAhead)->getY() <= y; ++readAhead) {} // Moving only
1075  for (; readAhead != _spans.end() && (*readAhead)->getY() == y + 1; ++readAhead) {
1076  std::fill(rowAfter.begin() + (*readAhead)->getX0() - xStart,
1077  rowAfter.begin() + (*readAhead)->getX1() + 1 - xStart,
1078  true);
1079  }
1080  yLast = y;
1081  }
1082 
1083  // Look for edge in the current row
1084  int x0 = (*ss)->getX0();
1085  bool onEdge = true; // Are we on an edge? The first pixel is an edge
1086  for (int x = x0 + 1, i = x0 + 1 - xStart; x < (*ss)->getX1(); ++x, ++i) {
1087  if (onEdge) {
1088  if (rowBefore[i] && rowAfter[i]) {
1089  // We've come to the end of the edge
1090  onEdge = false;
1091  edges->addSpanInSeries(y, x0, x - 1);
1092  }
1093  } else if (!rowBefore[i] || !rowAfter[i]) {
1094  // We're on an edge now
1095  onEdge = true;
1096  x0 = x;
1097  }
1098  }
1099  // Last pixel is an edge
1100  int const x1 = (*ss)->getX1();
1101  if (onEdge) {
1102  edges->addSpanInSeries(y, x0, x1);
1103  } else {
1104  edges->addSpanInSeries(y, x1, x1);
1105  }
1106  }
1107  edges->normalize(); // Should be a no-op, but just in case...
1108 
1109  return edges;
1110 }
1111 
1112 
1118 bool _checkNormalized(Footprint const& foot) {
1119  Footprint copy(foot);
1120  copy.normalize();
1121  if (copy.getArea() != foot.getArea()) {
1122  return false;
1123  }
1124  if (copy.getSpans().size() != foot.getSpans().size()) {
1125  return false;
1126  }
1127  Footprint::SpanList const& spansa = foot.getSpans();
1128  Footprint::SpanList const& spansb = copy.getSpans();
1129  Footprint::SpanList::const_iterator spa = spansa.begin();
1130  Footprint::SpanList::const_iterator spb = spansb.begin();
1131  for (; spa != spansa.end(); ++spa, ++spb) {
1132  if ((*spa)->getY() != (*spb)->getY()) {
1133  return false;
1134  }
1135  if ((*spa)->getX0() != (*spb)->getX0()) {
1136  return false;
1137  }
1138  if ((*spa)->getX1() != (*spb)->getX1()) {
1139  return false;
1140  }
1141  }
1142  return true;
1143 }
1144 
1145 /************************************************************************************************************/
1146 
1147 template<typename MaskT>
1149  PTR(Footprint) const& fp,
1150  typename lsst::afw::image::Mask<MaskT>::Ptr const& mask,
1151  MaskT const bitmask
1152 ) {
1153  PTR(Footprint) newFp(new Footprint(fp->getPeaks().getSchema()));
1154  return newFp;
1155 }
1156 
1157 /************************************************************************************************************/
1158 
1159 template<typename MaskT>
1160 MaskT setMaskFromFootprint(
1161  image::Mask<MaskT> *mask,
1162  Footprint const& foot,
1163  MaskT const bitmask
1164 ) {
1165 
1166  int const width = static_cast<int>(mask->getWidth());
1167  int const height = static_cast<int>(mask->getHeight());
1168 
1169  for (Footprint::SpanList::const_iterator siter = foot.getSpans().begin();
1170  siter != foot.getSpans().end(); ++siter) {
1171  CONST_PTR(Span) span = *siter;
1172  int const y = span->getY() - mask->getY0();
1173  if (y < 0 || y >= height) {
1174  continue;
1175  }
1176 
1177  int x0 = span->getX0() - mask->getX0();
1178  int x1 = span->getX1() - mask->getX0();
1179  x0 = (x0 < 0) ? 0 : (x0 >= width ? width - 1 : x0);
1180  x1 = (x1 < 0) ? 0 : (x1 >= width ? width - 1 : x1);
1181 
1182  for (typename image::Image<MaskT>::x_iterator ptr = mask->x_at(x0, y),
1183  end = mask->x_at(x1 + 1, y); ptr != end; ++ptr) {
1184  *ptr |= bitmask;
1185  }
1186  }
1187 
1188  return bitmask;
1189 }
1190 
1191 /************************************************************************************************************/
1192 
1193 template<typename MaskT>
1195  image::Mask<MaskT> *mask,
1196  Footprint const& foot,
1197  MaskT const bitmask
1198 ) {
1199  int const width = static_cast<int>(mask->getWidth());
1200  int const height = static_cast<int>(mask->getHeight());
1201 
1202  for (Footprint::SpanList::const_iterator siter = foot.getSpans().begin();
1203  siter != foot.getSpans().end(); ++siter) {
1204  CONST_PTR(Span) span = *siter;
1205  int const y = span->getY() - mask->getY0();
1206  if (y < 0 || y >= height) {
1207  continue;
1208  }
1209 
1210  int x0 = span->getX0() - mask->getX0();
1211  int x1 = span->getX1() - mask->getX0();
1212  x0 = (x0 < 0) ? 0 : (x0 >= width ? width - 1 : x0);
1213  x1 = (x1 < 0) ? 0 : (x1 >= width ? width - 1 : x1);
1214 
1215  for (typename image::Image<MaskT>::x_iterator ptr = mask->x_at(x0, y),
1216  end = mask->x_at(x1 + 1, y); ptr != end; ++ptr) {
1217  *ptr &= ~bitmask;
1218  }
1219  }
1220 
1221  return bitmask;
1222 }
1223 
1224 /************************************************************************************************************/
1225 
1226 template<typename MaskT>
1228  image::Mask<MaskT> *mask,
1229  std::vector<PTR(Footprint)> const& footprints,
1230  MaskT const bitmask
1231 ) {
1232  for (std::vector<PTR(Footprint)>::const_iterator fiter = footprints.begin();
1233  fiter != footprints.end(); ++fiter) {
1234  (void)setMaskFromFootprint(mask, **fiter, bitmask);
1235  }
1236 
1237  return bitmask;
1238 }
1239 
1240 /************************************************************************************************************/
1241 
1242 template<typename MaskT>
1244  image::Mask<MaskT> *mask,
1245  CONST_PTR(std::vector<PTR(Footprint)>) const & footprints,
1246  MaskT const bitmask
1247  ) {
1248  return setMaskFromFootprintList(mask, *footprints, bitmask);
1249 }
1250 
1251 /************************************************************************************************************/
1252 namespace {
1253 template<typename ImageT>
1254 class SetFootprint : public FootprintFunctor<ImageT> {
1255 public:
1256  SetFootprint(ImageT const& image,
1257  typename ImageT::Pixel value) :
1258  FootprintFunctor<ImageT>(image), _value(value) {}
1259 
1260 
1261  void operator()(typename ImageT::xy_locator loc, int, int) {
1262  *loc = _value;
1263  }
1264 private:
1265  typename ImageT::Pixel _value;
1266 };
1267 }
1268 
1269 template<typename ImageT>
1270 typename ImageT::Pixel setImageFromFootprint(
1271  ImageT *image,
1272  Footprint const& foot,
1273  typename ImageT::Pixel const value
1274 ) {
1275  SetFootprint<ImageT> setit(*image, value);
1276  setit.apply(foot);
1277 
1278  return value;
1279 }
1280 
1281 template<typename ImageT>
1282 typename ImageT::Pixel setImageFromFootprintList(
1283  ImageT *image,
1284  CONST_PTR(std::vector<PTR(Footprint)>) footprints,
1285  typename ImageT::Pixel const value
1286  ) {
1287  return setImageFromFootprintList(image, *footprints, value);
1288 }
1289 
1290 template<typename ImageT>
1291 typename ImageT::Pixel setImageFromFootprintList(
1292  ImageT *image,
1293  std::vector<PTR(Footprint)> const& footprints,
1294  typename ImageT::Pixel const value
1295 ) {
1296  SetFootprint<ImageT> setit(*image, value);
1297  for (std::vector<PTR(Footprint)>::const_iterator fiter = footprints.begin(),
1298  end = footprints.end(); fiter != end; ++fiter) {
1299  setit.apply(**fiter);
1300  }
1301 
1302  return value;
1303 }
1304 
1305 /************************************************************************************************************/
1306 /*
1307  * Worker routine for the pmSetFootprintArrayIDs/pmSetFootprintID (and pmMergeFootprintArrays)
1308  */
1309 template <typename IDPixelT>
1310 static void set_footprint_id(
1311  typename image::Image<IDPixelT>::Ptr idImage, // the image to set
1312  Footprint const& foot, // the footprint to insert
1313  int const id, // the desired ID
1314  int dx=0, int dy=0 // Add these to all x/y in the Footprint
1315 ) {
1316  for (Footprint::SpanList::const_iterator i = foot.getSpans().begin();
1317  i != foot.getSpans().end(); ++i) {
1318  CONST_PTR(Span) span = *i;
1319  for (typename image::Image<IDPixelT>::x_iterator ptr =
1320  idImage->x_at(span->getX0() + dx, span->getY() + dy),
1321  end = ptr + span->getWidth(); ptr != end; ++ptr) {
1322  *ptr = id;
1323  }
1324  }
1325 }
1326 
1327 template <typename IDPixelT>
1328 static void
1329 set_footprint_array_ids(
1330  typename image::Image<IDPixelT>::Ptr idImage, // the image to set
1331  std::vector<PTR(Footprint)> const& footprints, // the footprints to insert
1332  bool const relativeIDs // show IDs starting at 0, not Footprint->id
1333 ) {
1334  int id = 0; // first index will be 1
1335 
1336  for (std::vector<PTR(Footprint)>::const_iterator fiter = footprints.begin();
1337  fiter != footprints.end(); ++fiter) {
1338  CONST_PTR(Footprint) foot = *fiter;
1339 
1340  if (relativeIDs) {
1341  ++id;
1342  } else {
1343  id = foot->getId();
1344  }
1345 
1346  set_footprint_id<IDPixelT>(idImage, *foot, id);
1347  }
1348 }
1349 
1350 template void set_footprint_array_ids<int>(
1351  image::Image<int>::Ptr idImage,
1352  std::vector<PTR(Footprint)> const& footprints,
1353  bool const relativeIDs);
1354 
1355 /******************************************************************************/
1356 /*
1357  * Set an image to the value of footprint's ID wherever they may fall
1358  *
1359  * @param footprints the footprints to insert
1360  * @param relativeIDs show the IDs starting at 1, not pmFootprint->id
1361  */
1362 template <typename IDImageT>
1363 typename std::shared_ptr<image::Image<IDImageT> > setFootprintArrayIDs(
1364  std::vector<PTR(Footprint)> const& footprints,
1365  bool const relativeIDs
1366 ) {
1367  std::vector<PTR(Footprint)>::const_iterator fiter = footprints.begin();
1368  if (fiter == footprints.end()) {
1369  throw LSST_EXCEPT(
1370  lsst::pex::exceptions::InvalidParameterError,
1371  "You didn't provide any footprints"
1372  );
1373  }
1374  CONST_PTR(Footprint) foot = *fiter;
1375 
1376  typename image::Image<IDImageT>::Ptr idImage(
1377  new image::Image<IDImageT>(foot->getRegion())
1378  );
1379  *idImage = 0;
1380  /*
1381  * do the work
1382  */
1383  set_footprint_array_ids<IDImageT>(idImage, footprints, relativeIDs);
1384 
1385  return idImage;
1386 }
1387 
1388 template image::Image<int>::Ptr setFootprintArrayIDs(
1389  std::vector<PTR(Footprint)> const& footprints,
1390  bool const relativeIDs);
1391 /*
1392  * Set an image to the value of Footprint's ID wherever it may fall
1393  */
1394 template <typename IDImageT>
1395 typename std::shared_ptr<image::Image<IDImageT> > setFootprintID(
1396  CONST_PTR(Footprint)& foot, // the Footprint to insert
1397  int const id // the desired ID
1398  ) {
1399  typename image::Image<IDImageT>::Ptr idImage(new image::Image<IDImageT>(foot->getBBox()));
1400  *idImage = 0;
1401  /*
1402  * do the work
1403  */
1404  set_footprint_id<IDImageT>(idImage, *foot, id);
1405 
1406  return idImage;
1407 }
1408 
1409 template image::Image<int>::Ptr setFootprintID(CONST_PTR(Footprint)& foot, int const id);
1410 
1411 namespace {
1418 class StructuringElement
1419 {
1420 public:
1421  enum class Shape { CIRCLE, DIAMOND };
1422  typedef std::vector<Span>::const_iterator const_iterator;
1423  StructuringElement(Shape shape, int radius);
1424  StructuringElement(int left, int right, int up, int down);
1425  const_iterator begin() const { return _widths.begin(); }
1426  const_iterator end() const { return _widths.end(); }
1427  int getYRange() const { return _yRange; }
1428 
1429 private:
1430  std::vector<Span> _widths;
1431  int _yRange;
1432 };
1433 
1439 StructuringElement::StructuringElement(Shape shape, int radius) {
1440  _yRange = 2*radius + 1;
1441  _widths.reserve(_yRange);
1442  switch (shape) {
1443  case Shape::CIRCLE:
1444  for (auto dy = -radius; dy <= radius; dy++) {
1445  int dx = static_cast<int>(sqrt(radius*radius - dy*dy));
1446  _widths.push_back(Span(dy, -dx, dx));
1447  }
1448  break;
1449  case Shape::DIAMOND:
1450  for (auto dy = -radius; dy <= radius; dy++) {
1451  int dx = radius - abs(dy);
1452  _widths.push_back(Span(dy, -dx, dx));
1453  }
1454  break;
1455  }
1456 }
1457 
1462 StructuringElement::StructuringElement(int left, int right, int up, int down) {
1463  _yRange = up + down + 1;
1464  _widths.reserve(_yRange);
1465  for (auto dy = 1; dy <= up; dy++) {
1466  _widths.push_back(Span(dy, 0, 0));
1467  }
1468  for (auto dy = -1; dy >= -down; dy--) {
1469  _widths.push_back(Span(dy, 0, 0));
1470  }
1471  _widths.push_back(Span(0, -left, right));
1472 }
1473 
1478 PTR(Footprint) growFootprintImpl(
1479  Footprint const& foot,
1480  StructuringElement const& element
1481 ) {
1482  // Create an empty footprint covering foot's region.
1483  PTR(Footprint) grown(new Footprint(0, foot.getRegion()));
1484 
1485  // We use a map of (y coordinate) to set of (xmin, xmax) pairs to describe
1486  // the spans being constructed. In this way, we ensure that the spans are
1487  // always sorted by increasing y, xmin.
1488  std::map<int, std::set<std::pair<int, int>>> spans;
1489 
1490  // Iterate over foot & structuring element building up a collection of
1491  // spans which should be added to the footprint.
1492  for (auto spanIter = foot.getSpans().cbegin(); spanIter != foot.getSpans().cend(); ++spanIter) {
1493  for (auto elementIter = element.begin(); elementIter != element.end(); ++elementIter) {
1494  int const xmin = (*spanIter)->getX0() + elementIter->getX0();
1495  int const xmax = (*spanIter)->getX1() + elementIter->getX1();
1496  int const yval = (*spanIter)->getY() + elementIter->getY();
1497  spans[yval].insert(std::make_pair(xmin, xmax));
1498 
1499  // Merge overlapping spans at this y coordinate, thereby ensuring
1500  // that the proto-footprint remains normalized.
1501  std::set<std::pair<int, int>> newSpans;
1502  for (auto span = spans[yval].cbegin(); span != spans[yval].cend(); ++span) {
1503  int const start = span->first;
1504  int end = span->second;
1505  // Check for end+1 because end value is inclusive. That is, if
1506  // one span terminates at x=N and another begins at x=N+1,
1507  // those spans are contiguous.
1508  while (span != spans[yval].cend() && span->first <= (end+1)) {
1509  end = std::max(end, span++->second);
1510  }
1511  newSpans.insert(std::make_pair(start, end));
1512  --span; // "rewind" to the last span included
1513  }
1514  std::swap(spans[yval], newSpans);
1515  }
1516  }
1517 
1518  // Now append the spans to the output footprint, making use of the fact
1519  // that they are already normalized.
1520  for (auto y = spans.cbegin(); y != spans.cend(); ++y) {
1521  for (auto x = y->second.cbegin(); x != y->second.cend(); ++x) {
1522  grown->addSpanInSeries(y->first, x->first, x->second);
1523  }
1524  }
1525 
1526  // Copy over peaks from the original footprint
1527  grown->getPeaks() = PeakCatalog(foot.getPeaks().getTable(), foot.getPeaks().begin(),
1528  foot.getPeaks().end(), true);
1529 
1530  return grown;
1531 }
1532 
1542 struct PrimaryRun {
1543  int m, y, xmin, xmax;
1544 };
1545 
1550 bool comparePrimaryRun(PrimaryRun const& first, PrimaryRun const& second) {
1551  if (first.y != second.y) {
1552  return first.y < second.y;
1553  } else if (first.m != second.m) {
1554  return first.m < second.m;
1555  } else {
1556  return first.xmin < second.xmin;
1557  }
1558 }
1559 
1560 class ComparePrimaryRunY{
1561 public:
1562  bool operator()(PrimaryRun const& pr, int yval) {
1563  return pr.y < yval;
1564  }
1565  bool operator()(int yval, PrimaryRun const& pr) {
1566  return yval < pr.y;
1567  }
1568 };
1569 
1570 class ComparePrimaryRunM{
1571 public:
1572  bool operator()(PrimaryRun const& pr, int mval) {
1573  return pr.m < mval;
1574  }
1575  bool operator()(int mval, PrimaryRun const& pr) {
1576  return mval < pr.m;
1577  }
1578 };
1579 
1584 PTR(Footprint) shrinkFootprintImpl(
1585  Footprint const& foot,
1586  StructuringElement const& element
1587 ) {
1588  // Create an empty FootprintSet covering the input region
1589  PTR(Footprint) shrunk(new Footprint(0, foot.getRegion()));
1590 
1591  // Calculate all possible primary runs.
1592  std::vector<PrimaryRun> primaryRuns;
1593  for (auto spanIter = foot.getSpans().begin(); spanIter != foot.getSpans().end(); ++spanIter) {
1594  int m = 0;
1595  for (auto it = element.begin(); it != element.end(); ++it, ++m) {
1596  if ((it->getX1() - it->getX0()) <= ((*spanIter)->getX1() - (*spanIter)->getX0())) {
1597  int xmin = (*spanIter)->getX0() - it->getX0();
1598  int xmax = (*spanIter)->getX1() - it->getX1();
1599  int y = (*spanIter)->getY() - it->getY();
1600  primaryRuns.push_back(PrimaryRun({m, y, xmin, xmax}));
1601  }
1602  }
1603  }
1604 
1605  // Iterate over the primary runs in such a way that we consider all values
1606  // of m for given y, then all m for y+1, etc.
1607  std::sort(primaryRuns.begin(), primaryRuns.end(), comparePrimaryRun);
1608 
1609  for (int y = primaryRuns.front().y; y <= primaryRuns.back().y; ++y) {
1610  auto yRange = std::equal_range(primaryRuns.begin(), primaryRuns.end(), y, ComparePrimaryRunY());
1611 
1612  // Discard runs for any value of y for which we find fewer groups than
1613  // M, the total Y range of the structuring element. This is step 3.1
1614  // of the Kim et al. algorithm.
1615  if (std::distance(yRange.first, yRange.second) < element.getYRange()) {
1616  continue;
1617  }
1618 
1619  // "good" runs are those which are covered by each value of m, ie by
1620  // each row in the structuring element. Our algorithm will consider
1621  // each value of m in turn, gradually whittling down the list of good
1622  // runs, then finally convert the remainder into Spans and add them to
1623  // the shrunken Footprint.
1624  std::list<PrimaryRun> goodRuns;
1625 
1626  for (int m = 0; m < element.getYRange(); ++m) {
1627  auto mRange = std::equal_range(yRange.first, yRange.second, m, ComparePrimaryRunM());
1628  if (mRange.first == mRange.second) {
1629  // If a particular m is missing, we know that this y contains
1630  // no good runs; this is equivalent to Kim et al. step 3.2.
1631  goodRuns.clear();
1632  } else {
1633  // Consolidate all primary runs at this m so that they
1634  // don't overlap.
1635  std::list<PrimaryRun> candidateRuns;
1636  int start_x = mRange.first->xmin;
1637  int end_x = mRange.first->xmax;
1638  for (auto run = mRange.first+1; run != mRange.second; ++run) {
1639  if (run->xmin > end_x) {
1640  // Start of a new run
1641  candidateRuns.push_back(PrimaryRun{m, y, start_x, end_x});
1642  start_x = run->xmin;
1643  end_x = run->xmax;
1644  } else {
1645  // Continuation of an existing run
1646  end_x = run->xmax;
1647  }
1648  }
1649  candidateRuns.push_back(PrimaryRun{m, y, start_x, end_x});
1650 
1651  // Otherwise, calculate the intersection of candidate runs at
1652  // this m with good runs from all previous m.
1653  if (m == 0) {
1654  // For m = 0 we have nothing to compare to; all runs are
1655  // accepted.
1656  std::swap(goodRuns, candidateRuns);
1657  } else {
1658  std::list<PrimaryRun> newlist;
1659  for (auto good = goodRuns.begin(); good != goodRuns.end(); ++good) {
1660  for (auto cand = candidateRuns.begin(); cand != candidateRuns.end(); ++cand) {
1661  int start = std::max(good->xmin, cand->xmin);
1662  int end = std::min(good->xmax, cand->xmax);
1663  if (end >= start) {
1664  newlist.push_back(PrimaryRun({m, y, start, end}));
1665  }
1666  }
1667  }
1668  std::swap(newlist, goodRuns);
1669  }
1670  }
1671  }
1672  for (auto run = goodRuns.begin(); run != goodRuns.end(); ++run) {
1673  shrunk->addSpan(run->y, run->xmin, run->xmax);
1674  }
1675  }
1676 
1677  shrunk->normalize();
1678 
1679  // Peaks from the original footprint have not yet been added to the shrunken footprint.
1680  // Iterate over peaks from the original footprint and add them IF they are contained
1681  // within the shrunken footprint.
1682  for (auto peakIter = foot.getPeaks().begin(); peakIter != foot.getPeaks().end(); peakIter++) {
1683  if (shrunk->contains(peakIter->getI())) {
1684  shrunk->getPeaks().addNew()->assign(*peakIter);
1685  }
1686  }
1687  return shrunk;
1688 }
1689 }
1690 
1691 /************************************************************************************************************/
1692 
1693 namespace {
1694  PTR(Footprint) _mergeFootprints(Footprint const& aFoot, Footprint const& bFoot) {
1695  PTR(Footprint) foot(new Footprint());
1696 
1697  const PeakCatalog& aPeak = aFoot.getPeaks();
1698  const PeakCatalog& bPeak = bFoot.getPeaks();
1699  PeakCatalog& peaks = foot->getPeaks();
1700  if (aPeak.empty()) {
1701  if (!bPeak.empty()) {
1702  peaks = PeakCatalog(bPeak.getTable(), bPeak.begin(), bPeak.end(), true);
1703  }
1704  } else {
1705  if (bPeak.empty()) {
1706  peaks = PeakCatalog(aPeak.getTable(), aPeak.begin(), aPeak.end(), true);
1707  } else {
1708  if (aPeak.getSchema() == bPeak.getSchema()) {
1709  // use schema A, as it's the same as schema B
1710  peaks = PeakCatalog(aPeak.getTable());
1711  peaks.reserve(aPeak.size() + bPeak.size());
1712  peaks.insert(peaks.end(), aPeak.begin(), aPeak.end(), true);
1713  peaks.insert(peaks.end(), bPeak.begin(), bPeak.end(), true);
1714  } else {
1715  throw LSST_EXCEPT(
1716  pex::exceptions::InvalidParameterError,
1717  "Cannot merge Footprints when Peaks have different Schemas"
1718  );
1719  }
1720  }
1721  }
1722 
1723  Footprint::SpanList const& aSpans = aFoot.getSpans();
1724  Footprint::SpanList const& bSpans = bFoot.getSpans();
1725  Footprint::SpanList::const_iterator aSpan = aSpans.begin();
1726  Footprint::SpanList::const_iterator bSpan = bSpans.begin();
1727  Footprint::SpanList::const_iterator aEnd = aSpans.end();
1728  Footprint::SpanList::const_iterator bEnd = bSpans.end();
1729 
1730  foot->getSpans().reserve(std::max(aSpans.size(), bSpans.size()));
1731 
1732  while ((aSpan != aEnd) && (bSpan != bEnd)) {
1733  int y = (*aSpan)->getY();
1734  int x0 = (*aSpan)->getX0();
1735  int x1 = (*aSpan)->getX1();
1736  int yb = (*bSpan)->getY();
1737  int xb0 = (*bSpan)->getX0();
1738  int xb1 = (*bSpan)->getX1();
1739 
1740  if ((y < yb) || (y == yb && (x1 < (xb0-1)))) {
1741  // A is earlier -- add A
1742  foot->addSpanInSeries(y, x0, x1);
1743  ++aSpan;
1744  continue;
1745  }
1746  if ((yb < y) || (y == yb && (xb1 < (x0-1)))) {
1747  // B is earlier -- add B
1748  foot->addSpanInSeries(yb, xb0, xb1);
1749  ++bSpan;
1750  continue;
1751  }
1752 
1753  assert(yb == y);
1754  // Overlap -- find connected spans from both iterators.
1755  x0 = std::min(x0, xb0);
1756  x1 = std::max(x1, xb1);
1757  // Union all connected spans
1758  ++aSpan;
1759  ++bSpan;
1760  while (true) {
1761  if ((aSpan != aEnd) &&
1762  ((*aSpan)->getY() == y) &&
1763  ((*aSpan)->getX0() <= (x1+1))) {
1764  // *aSpan continues this span.
1765  x1 = std::max(x1, (*aSpan)->getX1());
1766  ++aSpan;
1767  continue;
1768  }
1769  if ((bSpan != bEnd) &&
1770  ((*bSpan)->getY() == y) &&
1771  ((*bSpan)->getX0() <= (x1+1))) {
1772  // *bSpan continues this span.
1773  x1 = std::max(x1, (*bSpan)->getX1());
1774  ++bSpan;
1775  continue;
1776  }
1777  break;
1778  }
1779  foot->addSpanInSeries(y, x0, x1);
1780  }
1781  // At this point either "aSpan" or "bSpan" is at the end.
1782 
1783  // Add any remaining spans from "A".
1784  for (; aSpan != aEnd; ++aSpan) {
1785  foot->addSpanInSeries((*aSpan)->getY(), (*aSpan)->getX0(), (*aSpan)->getX1());
1786  }
1787  // Add any remaining spans from "B".
1788  for (; bSpan != bEnd; ++bSpan) {
1789  foot->addSpanInSeries((*bSpan)->getY(), (*bSpan)->getX0(), (*bSpan)->getX1());
1790  }
1791  return foot;
1792  }
1793 }
1794 
1795 PTR(Footprint) mergeFootprints(Footprint& foot1, Footprint& foot2) {
1796  foot1.normalize();
1797  foot2.normalize();
1798  return _mergeFootprints(foot1, foot2);
1799 }
1800 
1801 PTR(Footprint) mergeFootprints(Footprint const& foot1, Footprint const& foot2) {
1802  if (!foot1.isNormalized() || !foot2.isNormalized()) {
1803  throw LSST_EXCEPT(
1804  lsst::pex::exceptions::InvalidParameterError,
1805  "mergeFootprints(const Footprints) requires normalize()d Footprints.");
1806  }
1807  return _mergeFootprints(foot1, foot2);
1808 }
1809 
1810 /************************************************************************************************************/
1811 
1812 void nearestFootprint(std::vector<PTR(Footprint)> const& foots,
1815 {
1816  /*
1817  * insert the footprints into an image, set all the pixels to the
1818  * Manhattan distance from the nearest set pixel.
1819  */
1820  typedef std::uint16_t dtype;
1821  typedef std::uint16_t itype;
1822 
1823  const itype nil = 0xffff;
1824 
1825  const geom::Box2I bbox = argmin->getBBox();
1826  *argmin = 0;
1827  *dist = 0;
1828 
1829  int const x0 = bbox.getMinX();
1830  int const y0 = bbox.getMinY();
1831 
1832  for (size_t i=0; i<foots.size(); ++i) {
1833  // Set all the pixels in the footprint to 1
1834  set_footprint_id<itype>(argmin, *foots[i], i, -x0, -y0);
1835  set_footprint_id<dtype>(dist, *foots[i], 1, -x0, -y0);
1836  }
1837 
1838  int const height = dist->getHeight();
1839  int const width = dist->getWidth();
1840 
1841  // traverse from bottom left to top right
1842  for (int y = 0; y != height; ++y) {
1843  image::Image<dtype>::xy_locator dim = dist->xy_at(0, y);
1844  image::Image<itype>::xy_locator aim = argmin->xy_at(0, y);
1845  for (int x = 0; x != width; ++x, ++dim.x(), ++aim.x()) {
1846  if (dim(0, 0) == 1) {
1847  // first pass and pixel was on, it gets a zero
1848  dim(0, 0) = 0;
1849  // its argmin is already set
1850  } else {
1851  // pixel was off. It is at most the sum of lengths of
1852  // the array away from a pixel that is on
1853  dim(0, 0) = width + height;
1854  aim(0, 0) = nil;
1855  // or one more than the pixel to the north
1856  if (y > 0) {
1857  dtype ndist = dim(0,-1) + 1;
1858  if (ndist < dim(0,0)) {
1859  dim(0,0) = ndist;
1860  aim(0,0) = aim(0,-1);
1861  }
1862  }
1863  // or one more than the pixel to the west
1864  if (x > 0) {
1865  dtype ndist = dim(-1,0) + 1;
1866  if (ndist < dim(0,0)) {
1867  dim(0,0) = ndist;
1868  aim(0,0) = aim(-1,0);
1869  }
1870  }
1871  }
1872  }
1873  }
1874  // traverse from top right to bottom left
1875  for (int y = height - 1; y >= 0; --y) {
1876  image::Image<dtype>::xy_locator dim = dist->xy_at(width-1, y);
1877  image::Image<itype>::xy_locator aim = argmin->xy_at(width-1, y);
1878  for (int x = width - 1; x >= 0; --x, --dim.x(), --aim.x()) {
1879  // either what we had on the first pass or one more than
1880  // the pixel to the south
1881  if (y + 1 < height) {
1882  dtype ndist = dim(0,1) + 1;
1883  if (ndist < dim(0,0)) {
1884  dim(0,0) = ndist;
1885  aim(0,0) = aim(0,1);
1886  }
1887  }
1888  // or one more than the pixel to the east
1889  if (x + 1 < width) {
1890  dtype ndist = dim(1,0) + 1;
1891  if (ndist < dim(0,0)) {
1892  dim(0,0) = ndist;
1893  aim(0,0) = aim(1,0);
1894  }
1895  }
1896  }
1897  }
1898 }
1899 
1900 PTR(Footprint) growFootprint(
1901  Footprint const& foot,
1902  int nGrow,
1903  bool isotropic
1904 ) {
1905  if (nGrow <= 0 || foot.getNpix() == 0 ) {
1906  // Return a new footprint equal to the input.
1907  return PTR(Footprint)(new Footprint(foot));
1908  }
1909 
1910  // An isotropic grow is equivalent to growing with a circular structuring
1911  // element, while a Manhattan grow is equivalent to growing with a
1912  // diamond-shaped element.
1914  Shape shape = isotropic ? Shape::CIRCLE : Shape::DIAMOND;
1915  return growFootprintImpl(foot, StructuringElement(shape, nGrow));
1916 }
1917 
1918 PTR(Footprint) growFootprint(PTR(Footprint) const& foot, int nGrow, bool isotropic) {
1919  return growFootprint(*foot, nGrow, isotropic);
1920 }
1921 
1922 PTR(Footprint) growFootprint(Footprint const& foot,
1923  int nGrow,
1924  bool left,
1925  bool right,
1926  bool up,
1927  bool down
1928  )
1929 {
1930  if (nGrow <= 0 || foot.getNpix() == 0 ) {
1931  // Return a new footprint equal to the input.
1932  return PTR(Footprint)(new Footprint(foot));
1933  }
1934  return growFootprintImpl(foot, StructuringElement(left ? nGrow: 0, right ? nGrow : 0,
1935  up ? nGrow : 0, down ? nGrow : 0));
1936 }
1937 
1938 
1939 PTR(Footprint) shrinkFootprint(
1940  Footprint const& foot,
1941  int nShrink,
1942  bool isotropic
1943 ) {
1945  Shape shape = isotropic ? Shape::CIRCLE : Shape::DIAMOND;
1946  return shrinkFootprintImpl(foot, StructuringElement(shape, nShrink));
1947 }
1948 
1949 /************************************************************************************************************/
1950 
1951 std::vector<geom::Box2I> footprintToBBoxList(Footprint const& foot) {
1952  typedef std::uint16_t ImageT;
1953  geom::Box2I fpBBox = foot.getBBox();
1954  image::Image<ImageT>::Ptr idImage(
1955  new image::Image<ImageT>(fpBBox.getDimensions())
1956  );
1957  *idImage = 0;
1958  int const height = fpBBox.getHeight();
1959  geom::Extent2I shift(fpBBox.getMinX(), fpBBox.getMinY());
1960  foot.insertIntoImage(*idImage, 1, fpBBox);
1961 
1962  std::vector<geom::Box2I> bboxes;
1963  /*
1964  * Our strategy is to find a row of pixels in the Footprint and interpret it as the first
1965  * row of a rectangular set of pixels. We then extend this rectangle upwards as far as it
1966  * will go, and define that as a BBox. We clear all those pixels, and repeat until there
1967  * are none left. I.e. a Footprint will get cut up like this:
1968  *
1969  * .555...
1970  * 22.3314
1971  * 22.331.
1972  * .000.1.
1973  * (as shown in Footprint_1.py)
1974  */
1975 
1976  int y0 = 0; // the first row with non-zero pixels in it
1977  while (y0 < height) {
1978  geom::Box2I bbox; // our next BBox
1979  for (int y = y0; y != height; ++y) {
1980  // Look for a set pixel in this row
1981  image::Image<ImageT>::x_iterator begin = idImage->row_begin(y), end = idImage->row_end(y);
1982  image::Image<ImageT>::x_iterator first = std::find(begin, end, 1);
1983 
1984  if (first != end) { // A pixel is set in this row
1985  image::Image<ImageT>::x_iterator last = std::find(first, end, 0) - 1;
1986  int const x0 = first - begin;
1987  int const x1 = last - begin;
1988 
1989  std::fill(first, last + 1, 0); // clear pixels; we don't want to see them again
1990 
1991  bbox.include(geom::Point2I(x0, y)); // the LLC
1992  bbox.include(geom::Point2I(x1, y)); // the LRC; initial guess for URC
1993 
1994  // we found at least one pixel so extend the BBox upwards
1995  for (++y; y != height; ++y) {
1996  if (std::find(idImage->at(x0, y), idImage->at(x1 + 1, y), 0) != idImage->at(x1 + 1, y)) {
1997  break; // some pixels weren't set, so the BBox stops here, (actually in previous row)
1998  }
1999  std::fill(idImage->at(x0, y), idImage->at(x1 + 1, y), 0);
2000 
2001  bbox.include(geom::Point2I(x1, y)); // the new URC
2002  }
2003 
2004  bbox.shift(shift);
2005  bboxes.push_back(bbox);
2006  } else {
2007  y0 = y + 1;
2008  }
2009  break;
2010  }
2011  }
2012 
2013  return bboxes;
2014 }
2015 
2016 
2017 template<typename ImageOrMaskedImageT>
2018 void
2019 copyWithinFootprint(Footprint const& foot,
2020  PTR(ImageOrMaskedImageT) const input,
2021  PTR(ImageOrMaskedImageT) output) {
2022  Footprint::SpanList spans = foot.getSpans();
2023 
2024  int const inX0 = input->getX0(), inY0 = input->getY0();
2025  int const outX0 = output->getX0(), outY0 = output->getY0();
2026 
2027  int const xMin = std::max(inX0, outX0);
2028  int const xMax = std::min(input->getWidth() + inX0, output->getWidth() + outX0) - 1;
2029  for (Footprint::SpanList::iterator sp = spans.begin();
2030  sp != spans.end(); ++sp) {
2031  int const y = (*sp)->getY();
2032  int const x0 = (*sp)->getX0();
2033  int const x1 = (*sp)->getX1();
2034 
2035  int const yInput = y - inY0, yOutput = y - outY0;
2036  if (yInput < 0 || yInput >= input->getHeight() || yOutput < 0 || yOutput >= output->getHeight()) {
2037  continue;
2038  }
2039 
2040  int const xStart = std::max(x0, xMin); // Starting position in x, parent frame
2041  int const xStop = std::min(x1, xMax); // Stopping position (inclusive) in x, parent frame
2042 
2043  typename ImageOrMaskedImageT::const_x_iterator initer = input->x_at(xStart - inX0, yInput);
2044  typename ImageOrMaskedImageT::x_iterator outiter = output->x_at(xStart - outX0, yOutput);
2045  for (int x = xStart; x <= xStop; ++x, ++initer, ++outiter) {
2046  *outiter = *initer;
2047  }
2048  }
2049 }
2050 
2051 
2052 
2053 
2054 #if 0
2055 
2056 /************************************************************************************************************/
2057 /*
2058  * Grow a psArray of pmFootprints isotropically by r pixels, returning a new psArray of new pmFootprints
2059  */
2060 psArray *pmGrowFootprintArray(psArray const *footprints, // footprints to grow
2061  int r) { // how much to grow each footprint
2062  assert (footprints->n == 0 || pmIsFootprint(footprints->data[0]));
2063 
2064  if (footprints->n == 0) { // we don't know the size of the footprint's region
2065  return psArrayAlloc(0);
2066  }
2067  /*
2068  * We'll insert the footprints into an image, then convolve with a disk,
2069  * then extract a footprint from the result --- this is magically what we want.
2070  */
2071  psImage *idImage = pmSetFootprintArrayIDs(footprints, true);
2072  if (r <= 0) {
2073  r = 1; // r == 1 => no grow
2074  }
2075  psKernel *circle = psKernelAlloc(-r, r, -r, r);
2076  assert (circle->image->numRows == 2*r + 1 && circle->image->numCols == circle->image->numRows);
2077  for (int i = 0; i <= r; ++i) {
2078  for (int j = 0; j <= r; ++j) {
2079  if (i*i + j*j <= r*r) {
2080  circle->kernel[i][j] =
2081  circle->kernel[i][-j] =
2082  circle->kernel[-i][j] =
2083  circle->kernel[-i][-j] = 1;
2084  }
2085  }
2086  }
2087 
2088  psImage *grownIdImage = psImageConvolveDirect(idImage, circle); // Here's the actual grow step
2089  psFree(circle);
2090 
2091  psArray *grown = pmFindFootprints(grownIdImage, 0.5, 1); // and here we rebuild the grown footprints
2092  assert (grown != NULL);
2093  psFree(idImage);
2094  psFree(grownIdImage);
2095  /*
2096  * Now assign the peaks appropriately. We could do this more efficiently
2097  * using grownIdImage (which we just freed), but this is easy and probably fast enough
2098  */
2099  psArray const *peaks = pmFootprintArrayToPeaks(footprints);
2100  pmPeaksAssignToFootprints(grown, peaks);
2101  psFree((psArray *)peaks);
2102 
2103  return grown;
2104 }
2105 
2106 /************************************************************************************************************/
2107 /*
2108  * Merge together two psArrays of pmFootprints neither of which is damaged.
2109  *
2110  * The returned psArray may contain elements of the inital psArrays (with
2111  * their reference counters suitable incremented)
2112  */
2113 psArray *pmMergeFootprintArrays(psArray const *footprints1, // one set of footprints
2114  psArray const *footprints2, // the other set
2115  int const includePeaks) { // which peaks to set? 0x1 => footprints1, 0x2 => 2
2116  assert (footprints1->n == 0 || pmIsFootprint(footprints1->data[0]));
2117  assert (footprints2->n == 0 || pmIsFootprint(footprints2->data[0]));
2118 
2119  if (footprints1->n == 0 || footprints2->n == 0) { // nothing to do but put copies on merged
2120  psArray const *old = (footprints1->n == 0) ? footprints2 : footprints1;
2121 
2122  psArray *merged = psArrayAllocEmpty(old->n);
2123  for (int i = 0; i < old->n; ++i) {
2124  psArrayAdd(merged, 1, old->data[i]);
2125  }
2126 
2127  return merged;
2128  }
2129  /*
2130  * We have real work to do as some pmFootprints in footprints2 may overlap
2131  * with footprints1
2132  */
2133  {
2134  pmFootprint *fp1 = footprints1->data[0];
2135  pmFootprint *fp2 = footprints2->data[0];
2136  if (fp1->region.x0 != fp2->region.x0 ||
2137  fp1->region.x1 != fp2->region.x1 ||
2138  fp1->region.y0 != fp2->region.y0 ||
2139  fp1->region.y1 != fp2->region.y1) {
2140  psError(PS_ERR_BAD_PARAMETER_SIZE, true,
2141  "The two pmFootprint arrays correspnond to different-sized regions");
2142  return NULL;
2143  }
2144  }
2145  /*
2146  * We'll insert first one set of footprints then the other into an image, then
2147  * extract a footprint from the result --- this is magically what we want.
2148  */
2149  psImage *idImage = pmSetFootprintArrayIDs(footprints1, true);
2150  set_footprint_array_ids(idImage, footprints2, true);
2151 
2152  psArray *merged = pmFindFootprints(idImage, 0.5, 1);
2153  assert (merged != NULL);
2154  psFree(idImage);
2155  /*
2156  * Now assign the peaks appropriately. We could do this more efficiently
2157  * using idImage (which we just freed), but this is easy and probably fast enough
2158  */
2159  if (includePeaks & 0x1) {
2160  psArray const *peaks = pmFootprintArrayToPeaks(footprints1);
2161  pmPeaksAssignToFootprints(merged, peaks);
2162  psFree((psArray *)peaks);
2163  }
2164 
2165  if (includePeaks & 0x2) {
2166  psArray const *peaks = pmFootprintArrayToPeaks(footprints2);
2167  pmPeaksAssignToFootprints(merged, peaks);
2168  psFree((psArray *)peaks);
2169  }
2170 
2171  return merged;
2172 }
2173 
2174 /************************************************************************************************************/
2175 /*
2176  * Given a psArray of pmFootprints and another of pmPeaks, assign the peaks to the
2177  * footprints in which that fall; if they _don't_ fall in a footprint, add a suitable
2178  * one to the list.
2179  */
2180 psErrorCode
2181 pmPeaksAssignToFootprints(psArray *footprints, // the pmFootprints
2182  psArray const *peaks) { // the pmPeaks
2183  assert (footprints != NULL);
2184  assert (footprints->n == 0 || pmIsFootprint(footprints->data[0]));
2185  assert (peaks != NULL);
2186  assert (peaks->n == 0 || pmIsPeak(peaks->data[0]));
2187 
2188  if (footprints->n == 0) {
2189  if (peaks->n > 0) {
2190  return psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Your list of footprints is empty");
2191  }
2192  return PS_ERR_NONE;
2193  }
2194  /*
2195  * Create an image filled with the object IDs, and use it to assign pmPeaks to the
2196  * objects
2197  */
2198  psImage *ids = pmSetFootprintArrayIDs(footprints, true);
2199  assert (ids != NULL);
2200  assert (ids->type.type == PS_TYPE_S32);
2201  int const y0 = ids->y0;
2202  int const x0 = ids->x0;
2203  int const numRows = ids->numRows;
2204  int const numCols = ids->numCols;
2205 
2206  for (int i = 0; i < peaks->n; ++i) {
2207  pmPeak *peak = peaks->data[i];
2208  int const x = peak->x - x0;
2209  int const y = peak->y - y0;
2210 
2211  assert (x >= 0 && x < numCols && y >= 0 && y < numRows);
2212  int id = ids->data.S32[y][x - x0];
2213 
2214  if (id == 0) { // peak isn't in a footprint, so make one for it
2215  pmFootprint *nfp = pmFootprintAlloc(1, ids);
2216  pmFootprintAddSpan(nfp, y, x, x);
2217  psArrayAdd(footprints, 1, nfp);
2218  psFree(nfp);
2219  id = footprints->n;
2220  }
2221 
2222  assert (id >= 1 && id <= footprints->n);
2223  pmFootprint *fp = footprints->data[id - 1];
2224  psArrayAdd(fp->peaks, 5, peak);
2225  }
2226 
2227  psFree(ids);
2228  //
2229  // Make sure that peaks within each footprint are sorted and unique
2230  //
2231  for (int i = 0; i < footprints->n; ++i) {
2232  pmFootprint *fp = footprints->data[i];
2233  fp->peaks = psArraySort(fp->peaks, pmPeakSortBySN);
2234 
2235  for (int j = 1; j < fp->peaks->n; ++j) { // check for duplicates
2236  if (fp->peaks->data[j] == fp->peaks->data[j-1]) {
2237  (void)psArrayRemoveIndex(fp->peaks, j);
2238  j--; // we moved everything down one
2239  }
2240  }
2241  }
2242 
2243  return PS_ERR_NONE;
2244 }
2245 
2246 /************************************************************************************************************/
2247  /*
2248  * Examine the peaks in a pmFootprint, and throw away the ones that are not sufficiently
2249  * isolated. More precisely, for each peak find the highest coll that you'd have to traverse
2250  * to reach a still higher peak --- and if that coll's more than nsigma DN below your
2251  * starting point, discard the peak.
2252  */
2253 psErrorCode pmFootprintCullPeaks(psImage const *img, // the image wherein lives the footprint
2254  psImage const *weight, // corresponding variance image
2255  pmFootprint *fp, // Footprint containing mortal peaks
2256  float const nsigma_delta, // how many sigma above local background a peak
2257  // needs to be to survive
2258  float const min_threshold) { // minimum permitted coll height
2259  assert (img != NULL);
2260  assert (img->type.type == PS_TYPE_F32);
2261  assert (weight != NULL);
2262  assert (weight->type.type == PS_TYPE_F32);
2263  assert (img->y0 == weight->y0 && img->x0 == weight->x0);
2264  assert (fp != NULL);
2265 
2266  if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
2267  return PS_ERR_NONE;
2268  }
2269 
2270  psRegion subRegion; // desired subregion; 1 larger than bounding box (grr)
2271  subRegion.x0 = fp->bbox.x0;
2272  subRegion.x1 = fp->bbox.x1 + 1;
2273  subRegion.y0 = fp->bbox.y0;
2274  subRegion.y1 = fp->bbox.y1 + 1;
2275  psImage const *subImg = psImageSubset((psImage *)img, subRegion);
2276  psImage const *subWt = psImageSubset((psImage *)weight, subRegion);
2277  assert (subImg != NULL && subWt != NULL);
2278  //
2279  // We need a psArray of peaks brighter than the current peak. We'll fake this
2280  // by reusing the fp->peaks but lying about n.
2281  //
2282  // We do this for efficiency (otherwise I'd need two peaks lists), and we are
2283  // rather too chummy with psArray in consequence. But it works.
2284  //
2285  psArray *brightPeaks = psArrayAlloc(0);
2286  psFree(brightPeaks->data);
2287  brightPeaks->data = psMemIncrRefCounter(fp->peaks->data);// use the data from fp->peaks
2288  //
2289  // The brightest peak is always safe; go through other peaks trying to cull them
2290  //
2291  for (int i = 1; i < fp->peaks->n; ++i) { // n.b. fp->peaks->n can change within the loop
2292  pmPeak const *peak = fp->peaks->data[i];
2293  int x = peak->x - subImg->x0;
2294  int y = peak->y - subImg->y0;
2295  //
2296  // Find the level nsigma below the peak that must separate the peak
2297  // from any of its friends
2298  //
2299  assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
2300  float const stdev = std::sqrt(subWt->data.F32[y][x]);
2301  float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
2302  if (std::isnan(threshold) || threshold < min_threshold) {
2303 #if 1 // min_threshold is assumed to be below the detection threshold,
2304  // so all the peaks are pmFootprint, and this isn't the brightest
2305  (void)psArrayRemoveIndex(fp->peaks, i);
2306  i--; // we moved everything down one
2307  continue;
2308 #else
2309 #error n.b. We will be running LOTS of checks at this threshold, so only find the footprint once
2310  threshold = min_threshold;
2311 #endif
2312  }
2313  if (threshold > subImg->data.F32[y][x]) {
2314  threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
2315  }
2316 
2317  int const peak_id = 1; // the ID for the peak of interest
2318  brightPeaks->n = i; // only stop at a peak brighter than we are
2319  pmFootprint *peakFootprint = pmFindFootprintAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
2320  brightPeaks->n = 0; // don't double free
2321  psImage *idImg = pmSetFootprintID(peakFootprint, peak_id);
2322  psFree(peakFootprint);
2323 
2324  int j;
2325  for (j = 0; j < i; ++j) {
2326  pmPeak const *peak2 = fp->peaks->data[j];
2327  int x2 = peak2->x - subImg->x0;
2328  int y2 = peak2->y - subImg->y0;
2329  int const peak2_id = idImg->data.S32[y2][x2]; // the ID for some other peak
2330 
2331  if (peak2_id == peak_id) { // There's a brighter peak within the footprint above
2332  ; // threshold; so cull our initial peak
2333  (void)psArrayRemoveIndex(fp->peaks, i);
2334  i--; // we moved everything down one
2335  break;
2336  }
2337  }
2338  if (j == i) {
2339  ++j;
2340  }
2341 
2342  psFree(idImg);
2343  }
2344 
2345  brightPeaks->n = 0;
2346  psFree(brightPeaks);
2347  psFree((psImage *)subImg);
2348  psFree((psImage *)subWt);
2349 
2350  return PS_ERR_NONE;
2351 }
2352 
2353 /*
2354  * Cull an entire psArray of pmFootprints
2355  */
2356 psErrorCode
2357 pmFootprintArrayCullPeaks(psImage const *img, // the image wherein lives the footprint
2358  psImage const *weight, // corresponding variance image
2359  psArray *footprints, // array of pmFootprints
2360  float const nsigma_delta, // how many sigma above local background a peak
2361  // needs to be to survive
2362  float const min_threshold) { // minimum permitted coll height
2363  for (int i = 0; i < footprints->n; ++i) {
2364  pmFootprint *fp = footprints->data[i];
2365  if (pmFootprintCullPeaks(img, weight, fp, nsigma_delta, min_threshold) != PS_ERR_NONE) {
2366  return psError(PS_ERR_UNKNOWN, false, "Culling pmFootprint %d", fp->id);
2367  }
2368  }
2369 
2370  return PS_ERR_NONE;
2371 }
2372 
2373 /************************************************************************************************************/
2374 /*
2375  * Extract the peaks in a psArray of pmFootprints, returning a psArray of pmPeaks
2376  */
2377 psArray *pmFootprintArrayToPeaks(psArray const *footprints) {
2378  assert(footprints != NULL);
2379  assert(footprints->n == 0 || pmIsFootprint(footprints->data[0]));
2380 
2381  int npeak = 0;
2382  for (int i = 0; i < footprints->n; ++i) {
2383  pmFootprint const *fp = footprints->data[i];
2384  npeak += fp->peaks->n;
2385  }
2386 
2387  psArray *peaks = psArrayAllocEmpty(npeak);
2388 
2389  for (int i = 0; i < footprints->n; ++i) {
2390  pmFootprint const *fp = footprints->data[i];
2391  for (int j = 0; j < fp->peaks->n; ++j) {
2392  psArrayAdd(peaks, 1, fp->peaks->data[j]);
2393  }
2394  }
2395 
2396  return peaks;
2397 }
2398 #endif
2399 
2400 /************************************************************************************************************/
2401 //
2402 // Explicit instantiations
2403 // \cond
2404 //
2405 //
2406 template
2407 void Footprint::intersectMask(
2408  image::Mask<image::MaskPixel> const& mask,
2409  image::MaskPixel bitMask);
2410 
2411 template
2412 PTR(Footprint) footprintAndMask(
2413  PTR(Footprint) const& foot,
2414  image::Mask<image::MaskPixel>::Ptr const& mask,
2415  image::MaskPixel bitMask);
2416 
2417 template
2419  image::Mask<image::MaskPixel> *mask,
2420  CONST_PTR(std::vector<PTR(Footprint)>) const& footprints,
2421  image::MaskPixel const bitmask);
2422 template
2424  image::Mask<image::MaskPixel> *mask,
2425  std::vector<PTR(Footprint)> const& footprints,
2426  image::MaskPixel const bitmask);
2427 template image::MaskPixel setMaskFromFootprint(
2428  image::Mask<image::MaskPixel> *mask,
2429  Footprint const& foot, image::MaskPixel const bitmask);
2430 template image::MaskPixel clearMaskFromFootprint(
2431  image::Mask<image::MaskPixel> *mask,
2432  Footprint const& foot, image::MaskPixel const bitmask);
2433 
2434 #define INSTANTIATE_NUMERIC(TYPE) \
2435 template \
2436 TYPE setImageFromFootprint(image::Image<TYPE> *image, \
2437  Footprint const& footprint, \
2438  TYPE const value); \
2439 template \
2440 TYPE setImageFromFootprintList(image::Image<TYPE> *image, \
2441  std::vector<PTR(Footprint)> const& footprints, \
2442  TYPE const value); \
2443 template \
2444 TYPE setImageFromFootprintList(image::Image<TYPE> *image, \
2445  CONST_PTR(std::vector<PTR(Footprint)>) footprints, \
2446  TYPE const value); \
2447 template \
2448 void copyWithinFootprint(Footprint const&, \
2449  PTR(lsst::afw::image::Image<TYPE>) const, \
2450  PTR(lsst::afw::image::Image<TYPE>)); \
2451 template \
2452 void copyWithinFootprint(Footprint const&, \
2453  PTR(lsst::afw::image::MaskedImage<TYPE>) const, \
2454  PTR(lsst::afw::image::MaskedImage<TYPE>)); \
2455 template \
2456  void Footprint::clipToNonzero(lsst::afw::image::Image<TYPE> const&); \
2457 
2458 
2459 INSTANTIATE_NUMERIC(float);
2460 INSTANTIATE_NUMERIC(double);
2461 // There's no reason these shouldn't have setImageFromFootprint(), etc, instantiated
2462 INSTANTIATE_NUMERIC(std::uint16_t);
2463 INSTANTIATE_NUMERIC(int);
2464 INSTANTIATE_NUMERIC(std::uint64_t);
2465 
2466 
2467 #define INSTANTIATE_MASK(PIXEL) \
2468 template \
2469 void Footprint::insertIntoImage( \
2470  lsst::afw::image::Image<PIXEL>& idImage, \
2471  std::uint64_t const id, \
2472  geom::Box2I const& region=geom::Box2I() \
2473  ) const; \
2474 template \
2475 void Footprint::insertIntoImage( \
2476  lsst::afw::image::Image<PIXEL>& idImage, \
2477  std::uint64_t const id, \
2478  bool const overwriteId, long const idMask, \
2479  std::set<std::uint64_t> *oldIds, \
2480  geom::Box2I const& region=geom::Box2I() \
2481  ) const; \
2482 template \
2483 PIXEL Footprint::overlapsMask(image::Mask<PIXEL> const& mask) const
2484 
2485 INSTANTIATE_MASK(std::uint16_t);
2486 INSTANTIATE_MASK(int);
2487 INSTANTIATE_MASK(std::uint64_t);
2488 
2489 
2490 }}}
2491 // \endcond
2492 
2493 // LocalWords: SpanList
int y
int iter
Footprint(int nspan=0, geom::Box2I const &region=geom::Box2I())
std::uint16_t MaskPixel
int getMaxY() const
Definition: Box.h:129
Declare the Kernel class and subclasses.
Extent2I const getDimensions() const
Definition: Box.h:153
A coordinate class intended to represent absolute positions.
Represent a set of pixels of an arbitrary shape and size.
table::Key< std::string > name
Definition: ApCorrMap.cc:71
void shift(Extent2I const &offset)
Shift the position of the box by the given offset.
for(FootprintList::const_iterator ptr=feet->begin(), end=feet->end();ptr!=end;++ptr)
Definition: saturated.cc:82
tbl::Key< double > weight
x_iterator x_at(int x, int y) const
Return an x_iterator to the point (x, y) in the image.
Definition: Image.h:331
bool contains(Point2I const &point) const
Return true if the box contains the point.
CatalogT< BaseRecord > BaseCatalog
Definition: fwd.h:61
Box2I BoxI
Definition: Box.h:479
void include(Point2I const &point)
Expand this to ensure that this-&gt;contains(point).
geom::Point2D skyToPixel(geom::Angle sky1, geom::Angle sky2) const
Convert from sky coordinates (e.g. RA/dec) to pixel positions.
Definition: Wcs.cc:796
boost::shared_ptr< Footprint > footprintAndMask(boost::shared_ptr< Footprint > const &foot, typename image::Mask< MaskT >::Ptr const &mask, MaskT const bitmask)
Return a Footprint that&#39;s the intersection of a Footprint with a Mask.
float _value
Definition: saturated.cc:31
Point< double, 2 > Point2D
Definition: Point.h:288
afw::geom::ellipses::Quadrupole Shape
Definition: constants.h:58
geom::Box2I getBBox(ImageOrigin origin=PARENT) const
Definition: Image.h:379
Implementation of the WCS standard for a any projection.
Definition: Wcs.h:107
Point< int, 2 > Point2I
Definition: PSF.h:39
void swap(Mask< PixelT > &a, Mask< PixelT > &b)
Definition: Mask.cc:524
boost::shared_ptr< coord::Coord > pixelToSky(double pix1, double pix2) const
Convert from pixel position to sky coordinates (e.g. RA/dec)
Definition: Wcs.cc:886
#define CONST_PTR(...)
Definition: base.h:47
Extent< int, 2 > Extent2I
Definition: Extent.h:358
int getX0() const
Definition: Image.h:249
int const x0
Definition: saturated.cc:45
A coordinate class intended to represent absolute positions.
Definition: PSF.h:39
bool val
An integer coordinate rectangle.
Definition: Box.h:53
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
Represent a collections of footprints associated with image data.
boost::shared_ptr< Footprint > shrinkFootprint(Footprint const &foot, int nGrow, bool isotropic)
boost::shared_ptr< Footprint > growFootprint(Footprint const &foot, int nGrow, bool isotropic=true)
afw::table::CatalogT< PeakRecord > PeakCatalog
Definition: Peak.h:225
int getMinY() const
Definition: Box.h:125
geom::Box2I const & _bbox
Definition: fits_io_mpl.h:80
void include(Point2D const &point)
Expand this to ensure that this-&gt;contains(point).
int getMinX() const
Definition: Box.h:124
if(width!=gim.getWidth()||height!=gim.getHeight()||x0!=gim.getX0()||y0!=gim.getY0())
Definition: saturated.cc:47
int getHeight() const
Return the number of rows in the image.
Definition: Image.h:241
double x
bool isEmpty() const
Return true if the box contains no points.
Definition: Box.h:166
CatalogIterator< typename Internal::const_iterator > const_iterator
Definition: Catalog.h:109
MaskT clearMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
(AND ~bitmask) all the Mask&#39;s pixels that are in the Footprint; that is, set to zero in the Mask-inte...
LSST bitmasks.
ImageT::Pixel setImageFromFootprint(ImageT *image, Footprint const &footprint, typename ImageT::Pixel const value)
Set all image pixels in a Footprint to a given value.
int getWidth() const
Definition: Box.h:154
void reserve(size_type n)
Increase the capacity of the catalog to the given size.
Definition: Catalog.h:422
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
std::vector< lsst::afw::geom::Box2I > footprintToBBoxList(Footprint const &foot)
tuple m
Definition: lsstimport.py:48
int getHeight() const
Definition: Box.h:155
Extent< int, N > floor(Extent< double, N > const &input)
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition: Persistable.h:45
lsst::afw::detection::Footprint Footprint
Definition: Source.h:60
#define PTR(...)
Definition: base.h:41
MaskT setMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels that are in the Footprint.
int id
Definition: CR.cc:157
afw::table::Key< double > b
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Definition: BaseRecord.h:132
xy_locator xy_at(int x, int y) const
Definition: Image.h:353
x_iterator row_begin(int y) const
Definition: Image.h:321
int getMaxX() const
Definition: Box.h:128
A floating-point coordinate rectangle geometry.
Definition: Box.h:271
Include files required for standard LSST Exception handling.
boost::shared_ptr< Footprint > mergeFootprints(Footprint const &foot1, Footprint const &foot2)
int const y0
Definition: saturated.cc:45
Record class that represents a peak in a Footprint.
Definition: Peak.h:40
void nearestFootprint(std::vector< boost::shared_ptr< Footprint >> const &foots, lsst::afw::image::Image< std::uint16_t >::Ptr argmin, lsst::afw::image::Image< std::uint16_t >::Ptr dist)
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:239
A class to represent a 2-dimensional array of pixels.
Definition: PSF.h:43
Utility functions for kernels.
void copyWithinFootprint(Footprint const &foot, boost::shared_ptr< ImageOrMaskedImageT > const input, boost::shared_ptr< ImageOrMaskedImageT > output)
ImageT::Pixel setImageFromFootprintList(ImageT *image, boost::shared_ptr< std::vector< boost::shared_ptr< Footprint >> const > footprints, typename ImageT::Pixel const value)
Set all image pixels in a set of Footprints to a given value.
MaskT setMaskFromFootprintList(lsst::afw::image::Mask< MaskT > *mask, std::vector< boost::shared_ptr< Footprint >> const &footprints, MaskT const bitmask)
OR bitmask into all the Mask&#39;s pixels which are in the set of Footprints.
int getY0() const
Definition: Image.h:257