LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
FootprintSet.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
45 #include <algorithm>
46 #include <cassert>
47 #include <set>
48 #include <string>
49 #include <typeinfo>
50 #include "boost/format.hpp"
51 #include "lsst/pex/exceptions.h"
52 #include "lsst/pex/logging/Trace.h"
60 
61 namespace detection = lsst::afw::detection;
62 namespace image = lsst::afw::image;
63 namespace math = lsst::afw::math;
64 namespace pexLogging = lsst::pex::logging;
65 namespace geom = lsst::afw::geom;
66 
67 /************************************************************************************************************/
68 namespace {
70 
71  typedef boost::uint64_t IdPixelT; // Type of temporary Images used in merging Footprints
72 
73  struct Threshold_traits {
74  };
75  struct ThresholdLevel_traits : public Threshold_traits { // Threshold is a single number
76  };
77  struct ThresholdPixelLevel_traits : public Threshold_traits { // Threshold varies from pixel to pixel
78  };
79  struct ThresholdBitmask_traits : public Threshold_traits { // Threshold ORs with a bitmask
80  };
81 
82  //
83  // Define our own functions to handle NaN tests; this gives us the
84  // option to define a value for e.g. image::MaskPixel or int
85  //
86  template<typename T>
87  inline bool isBadPixel(T) {
88  return false;
89  }
90 
91  template<>
92  inline bool isBadPixel(float val) {
93  return std::isnan(val);
94  }
95 
96  template<>
97  inline bool isBadPixel(double val) {
98  return std::isnan(val);
99  }
100 
101  /*
102  * Return the number of bits required to represent a unsigned long
103  */
104  int nbit(unsigned long i) {
105  int n = 0;
106  while (i > 0) {
107  ++n;
108  i >>= 1;
109  }
110 
111  return n;
112  }
113  /*
114  * Find the list of pixel values that lie in a Footprint
115  *
116  * Used when the Footprints are constructed from an Image containing Footprint indices
117  */
118  template <typename ImageT>
119  class FindIdsInFootprint: public detection::FootprintFunctor<ImageT> {
120  public:
121  explicit FindIdsInFootprint(ImageT const& image
122  ) : detection::FootprintFunctor<ImageT>(image), _ids(), _old(0) {}
124  void reset() {
125  _ids.clear();
126  _old = 0;
127  }
128 
130  void operator()(typename ImageT::xy_locator loc,
131  int x,
132  int y
133  ) {
134  typename ImageT::Pixel val = loc(0, 0);
135 
136  if (val != _old) {
137  _ids.insert(val);
138  _old = val;
139  }
140  }
141 
142  std::set<typename ImageT::Pixel> const& getIds() const {
143  return _ids;
144  }
145 
146  private:
147  std::set<typename ImageT::Pixel> _ids;
148  typename ImageT::Pixel _old;
149  };
150 
151  /********************************************************************************************************/
152  /*
153  * Sort peaks by decreasing pixel value. N.b. -ve peaks are sorted the same way as +ve ones
154  */
155  struct SortPeaks {
156  bool operator()(CONST_PTR(detection::Peak) a, CONST_PTR(detection::Peak) b) {
157  if (a->getPeakValue() != b->getPeakValue()) {
158  return (a->getPeakValue() > b->getPeakValue());
159  }
160 
161  if (a->getIx() != b->getIx()) {
162  return (a->getIx() < b->getIx());
163  }
164 
165  return (a->getIy() < b->getIy());
166  }
167  };
168  struct ComparPeaks {
169  bool operator()(CONST_PTR(detection::Peak) a, CONST_PTR(detection::Peak) b)
170  {
171  return a->getId() < b->getId();
172  }
173  };
174  struct EqualPeaks {
175  int operator()(CONST_PTR(detection::Peak) a, CONST_PTR(detection::Peak) b)
176  {
177  return *a == *b;
178  }
179  };
180  /*********************************************************************************************************/
181  /*
182  * Worker routine for merging two FootprintSets, possibly growing them as we proceed
183  */
185  mergeFootprintSets(
186  detection::FootprintSet const &lhs, // the FootprintSet to be merged to
187  int rLhs, // Grow lhs Footprints by this many pixels
188  detection::FootprintSet const &rhs, // the FootprintSet to be merged into lhs
189  int rRhs, // Grow rhs Footprints by this many pixels
190  detection::FootprintControl const& ctrl // Control how the grow is done
191  )
192  {
195  // The isXXX routines return <isset, value>
196  bool const circular = ctrl.isCircular().first && ctrl.isCircular().second;
197  bool const isotropic = ctrl.isIsotropic().second; // isotropic grow as opposed to a Manhattan metric
198  // n.b. Isotropic grows are significantly slower
199  bool const left = ctrl.isLeft().first && ctrl.isLeft().second;
200  bool const right = ctrl.isRight().first && ctrl.isRight().second;
201  bool const up = ctrl.isUp().first && ctrl.isUp().second;
202  bool const down = ctrl.isDown().first && ctrl.isDown().second;
203 
204  geom::Box2I const region = lhs.getRegion();
205  if (region != rhs.getRegion()) {
206  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
207  boost::format("The two FootprintSets must have the same region").str());
208  }
209 
211  idImage->setXY0(region.getMinX(), region.getMinY());
212  *idImage = 0;
213 
214  FootprintList const& lhsFootprints = *lhs.getFootprints();
215  FootprintList const& rhsFootprints = *rhs.getFootprints();
216  int const nLhs = lhsFootprints.size();
217  int const nRhs = rhsFootprints.size();
218  /*
219  * In general the lists of Footprints overlap, so we need to make sure that the IDs can be
220  * uniquely recovered from the idImage. We do this by allocating a range of bits to the lhs IDs
221  */
222  int const lhsIdNbit = nbit(nLhs);
223  int const lhsIdMask = (lhsIdNbit == 0) ? 0x0 : (1 << lhsIdNbit) - 1;
224 
225  if (std::size_t(nRhs << lhsIdNbit) > std::numeric_limits<IdPixelT>::max() - 1) {
226  throw LSST_EXCEPT(lsst::pex::exceptions::OverflowError,
227  (boost::format("%d + %d footprints need too many bits; change IdPixelT typedef")
228  % nLhs % nRhs).str());
229  }
230  /*
231  * When we insert grown Footprints into the idImage we can potentially overwrite an entire Footprint,
232  * losing any peaks that it might contain. We'll preserve the overwritten Ids in case we need to
233  * get them back (n.b. Footprints that overlap, but both if which survive, will appear in this list)
234  */
235  typedef std::map<int, std::set<boost::uint64_t> > OldIdMap;
236  OldIdMap overwrittenIds; // here's a map from id -> overwritten IDs
237 
238  IdPixelT id = 1; // the ID inserted into the image
239  for (FootprintList::const_iterator ptr = lhsFootprints.begin(), end = lhsFootprints.end();
240  ptr != end; ++ptr, ++id) {
241  CONST_PTR(Footprint) foot = *ptr;
242 
243  if (rLhs > 0) {
244  foot = circular ?
245  growFootprint(*foot, rLhs, isotropic) : growFootprint(*foot, rLhs, left, right, up, down);
246  }
247 
248  std::set<boost::uint64_t> overwritten;
249  foot->insertIntoImage(*idImage, id, true, 0x0, &overwritten);
250 
251  if (!overwritten.empty()) {
252  overwrittenIds.insert(overwrittenIds.end(), std::make_pair(id, overwritten));
253  }
254  }
255 
256  assert (id <= std::size_t(1 << lhsIdNbit));
257  id = (1 << lhsIdNbit);
258  for (FootprintList::const_iterator ptr = rhsFootprints.begin(), end = rhsFootprints.end();
259  ptr != end; ++ptr, id += (1 << lhsIdNbit)) {
260  CONST_PTR(Footprint) foot = *ptr;
261 
262  if (rRhs > 0) {
263  foot = circular ?
264  growFootprint(*foot, rRhs, isotropic) : growFootprint(*foot, rRhs, left, right, up, down);
265  }
266 
267  std::set<boost::uint64_t> overwritten;
268  foot->insertIntoImage(*idImage, id, true, lhsIdMask, &overwritten);
269 
270  if (!overwritten.empty()) {
271  overwrittenIds.insert(overwrittenIds.end(), std::make_pair(id, overwritten));
272  }
273  }
274 
276  1, false); // detect all pixels in rhs + lhs
277  /*
278  * Now go through the new Footprints looking up and remembering their progenitor's IDs; we'll use
279  * these IDs to merge the peaks in a moment
280  *
281  * We can't do this as we go through the idFinder as the IDs it returns are
282  * (lhsId + 1) | ((rhsId + 1) << nbit)
283  * and, depending on the geometry, values of lhsId and/or rhsId can appear multiple times
284  * (e.g. if nbit is 2, idFinder IDs 0x5 and 0x6 both contain lhsId = 0) so we get duplicates
285  * of peaks. This is not too bad, but it's a bit of a pain to make the lists unique again,
286  * and we avoid this by this two-step process.
287  */
288  FindIdsInFootprint<image::Image<IdPixelT> > idFinder(*idImage);
289  for (FootprintList::iterator ptr = fs.getFootprints()->begin(),
290  end = fs.getFootprints()->end(); ptr != end; ++ptr) {
291  PTR(Footprint) foot = *ptr;
292 
293  idFinder.apply(*foot); // find the (mangled) [lr]hsFootprint IDs that contribute to foot
294 
295  std::set<boost::uint64_t> lhsFootprintIndxs, rhsFootprintIndxs; // indexes into [lr]hsFootprints
296 
297  for (std::set<IdPixelT>::iterator idptr = idFinder.getIds().begin(),
298  idend = idFinder.getIds().end(); idptr != idend; ++idptr) {
299  unsigned int indx = *idptr;
300  if ((indx & lhsIdMask) > 0) {
301  boost::uint64_t i = (indx & lhsIdMask) - 1;
302  lhsFootprintIndxs.insert(i);
303  /*
304  * Now allow for Footprints that vanished beneath this one
305  */
306  OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
307  if (mapPtr != overwrittenIds.end()) {
308  std::set<boost::uint64_t> &overwritten = mapPtr->second;
309 
310  for (std::set<boost::uint64_t>::iterator ptr = overwritten.begin(),
311  end = overwritten.end(); ptr != end; ++ptr){
312  lhsFootprintIndxs.insert((*ptr & lhsIdMask) - 1);
313  }
314  }
315  }
316  indx >>= lhsIdNbit;
317 
318  if (indx > 0) {
319  boost::uint64_t i = indx - 1;
320  rhsFootprintIndxs.insert(i);
321  /*
322  * Now allow for Footprints that vanished beneath this one
323  */
324  OldIdMap::iterator mapPtr = overwrittenIds.find(indx);
325  if (mapPtr != overwrittenIds.end()) {
326  std::set<boost::uint64_t> &overwritten = mapPtr->second;
327 
328  for (std::set<boost::uint64_t>::iterator ptr = overwritten.begin(),
329  end = overwritten.end(); ptr != end; ++ptr) {
330  rhsFootprintIndxs.insert(*ptr - 1);
331  }
332  }
333  }
334  }
335  /*
336  * We now have a complete set of Footprints that contributed to this one, so merge
337  * all their Peaks into the new one
338  */
339  Footprint::PeakList &peaks = foot->getPeaks();
340 
341  for (std::set<boost::uint64_t>::iterator ptr = lhsFootprintIndxs.begin(),
342  end = lhsFootprintIndxs.end(); ptr != end; ++ptr) {
343  boost::uint64_t i = *ptr;
344  assert (i < lhsFootprints.size());
345  Footprint::PeakList const& oldPeaks = lhsFootprints[i]->getPeaks();
346 
347  int const nold = peaks.size();
348  peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
349  std::inplace_merge(peaks.begin(), peaks.begin() + nold, peaks.end(), SortPeaks());
350  }
351 
352  for (std::set<boost::uint64_t>::iterator ptr = rhsFootprintIndxs.begin(),
353  end = rhsFootprintIndxs.end(); ptr != end; ++ptr) {
354  boost::uint64_t i = *ptr;
355  assert (i < rhsFootprints.size());
356  Footprint::PeakList const& oldPeaks = rhsFootprints[i]->getPeaks();
357 
358  int const nold = peaks.size();
359  peaks.insert(peaks.end(), oldPeaks.begin(), oldPeaks.end());
360  std::inplace_merge(peaks.begin(), peaks.begin() + nold, peaks.end(), SortPeaks());
361  }
362  }
363 
364  return fs;
365  }
366 /*
367  * run-length code for part of object
368  */
369  class IdSpan {
370  public:
371  typedef boost::shared_ptr<IdSpan> Ptr;
372 
373  explicit IdSpan(int id, int y, int x0, int x1, double good) :
374  id(id), y(y), x0(x0), x1(x1), good(good) {}
375  int id; /* ID for object */
376  int y; /* Row wherein IdSpan dwells */
377  int x0, x1; /* inclusive range of columns */
378  bool good; /* includes a value over the desired threshold? */
379  };
380 /*
381  * comparison functor; sort by ID then row
382  */
383  struct IdSpanCompar : public std::binary_function<const IdSpan::Ptr, const IdSpan::Ptr, bool> {
384  bool operator()(IdSpan::Ptr const a, IdSpan::Ptr const b) {
385  if (a->id < b->id) {
386  return true;
387  } else if (a->id > b->id) {
388  return false;
389  } else {
390  return (a->y < b->y) ? true : false;
391  }
392  }
393  };
394 /*
395  * Follow a chain of aliases, returning the final resolved value.
396  */
397  int resolve_alias(std::vector<int> const &aliases, /* list of aliases */
398  int id) { /* alias to look up */
399  int resolved = id; /* resolved alias */
400 
401  while (id != aliases[id]) {
402  resolved = id = aliases[id];
403  }
404 
405  return(resolved);
406  }
408 }
409 
410 /************************************************************************************************************/
411 
412 namespace {
413  /*
414  * Find all the Peaks within a Footprint
415  */
416  template <typename ImageT>
417  class FindPeaksInFootprint: public detection::FootprintFunctor<ImageT> {
418  public:
419  explicit FindPeaksInFootprint(ImageT const& image,
420  bool polarity,
422  ) : detection::FootprintFunctor<ImageT>(image),
423  _polarity(polarity), _peaks(peaks) {}
424 
426  void operator()(typename ImageT::xy_locator loc,
427  int x,
428  int y
429  ) {
430  typename ImageT::Pixel val = loc(0, 0);
431 
432  if (_polarity) { // look for +ve peaks
433  if (loc(-1, 1) > val || loc( 0, 1) > val || loc( 1, 1) > val ||
434  loc(-1, 0) > val || loc( 1, 0) > val ||
435  loc(-1, -1) > val || loc( 0, -1) > val || loc( 1, -1) > val) {
436  return;
437  }
438  } else { // look for -ve "peaks" (pits)
439  if (loc(-1, 1) < val || loc( 0, 1) < val || loc( 1, 1) < val ||
440  loc(-1, 0) < val || loc( 1, 0) < val ||
441  loc(-1, -1) < val || loc( 0, -1) < val || loc( 1, -1) < val) {
442  return;
443  }
444  }
445 
446  _peaks.push_back(PTR(detection::Peak)(new detection::Peak(x, y, val)));
447  }
448  private:
449  bool _polarity;
451  };
452 
453  /*
454  * Find the maximum (or minimum, if polarity is false) pixel in a Footprint
455  */
456  template <typename ImageT>
457  class FindMaxInFootprint : public detection::FootprintFunctor<ImageT> {
458  public:
459  explicit FindMaxInFootprint(ImageT const& image,
460  bool polarity
461  ) : detection::FootprintFunctor<ImageT>(image),
462  _polarity(polarity), _x(0), _y(0),
463  _min( std::numeric_limits<double>::max()),
464  _max(-std::numeric_limits<double>::max()) {}
465 
467  void reset() {
468  _x = _y = 0;
471  }
472  virtual void reset(detection::Footprint const&) {}
473 
475  void operator()(typename ImageT::xy_locator loc,
476  int x,
477  int y
478  ) {
479  typename ImageT::Pixel val = loc(0, 0);
480 
481  if (_polarity) {
482  if (val > _max) {
483  _max = val;
484  _x = x;
485  _y = y;
486  }
487  } else {
488  if (val < _min) {
489  _min = val;
490  _x = x;
491  _y = y;
492  }
493  }
494  }
495 
497  PTR(detection::Peak) makePeak() const {
498  return boost::make_shared<detection::Peak>(detection::Peak(_x, _y, _polarity ? _max : _min));
499  }
500  private:
501  bool _polarity;
502  int _x, _y;
503  double _min, _max;
504  };
505 
506  template<typename ImageT, typename ThresholdT>
507  void findPeaks(PTR(detection::Footprint) foot, ImageT const& img, bool polarity, ThresholdT)
508  {
509  FindPeaksInFootprint<ImageT> peakFinder(img, polarity, foot->getPeaks());
510  peakFinder.apply(*foot, 1);
511 
512  std::stable_sort(foot->getPeaks().begin(), foot->getPeaks().end(), SortPeaks());
513 
514  if (foot->getPeaks().empty()) {
515  FindMaxInFootprint<ImageT> maxFinder(img, polarity);
516  maxFinder.apply(*foot);
517  foot->getPeaks().push_back(maxFinder.makePeak());
518  }
519  }
520 
521  // No need to search for peaks when processing a Mask
522  template<typename ImageT>
523  void findPeaks(PTR(detection::Footprint), ImageT const&, bool, ThresholdBitmask_traits)
524  {
525  ;
526  }
527 }
528 
529 /************************************************************************************************************/
530 /*
531  * Functions to determine if a pixel's in a Footprint
532  */
533 template<typename ImagePixelT, typename IterT>
534 static inline bool inFootprint(ImagePixelT pixVal, IterT,
535  bool polarity, double thresholdVal, ThresholdLevel_traits) {
536  return (polarity ? pixVal : -pixVal) >= thresholdVal;
537 }
538 
539 template<typename ImagePixelT, typename IterT>
540 static inline bool inFootprint(ImagePixelT pixVal, IterT var,
541  bool polarity, double thresholdVal, ThresholdPixelLevel_traits) {
542  return (polarity ? pixVal : -pixVal) >= thresholdVal*::sqrt(*var);
543 }
544 
545 template<typename ImagePixelT, typename IterT>
546 static inline bool inFootprint(ImagePixelT pixVal, IterT,
547  bool, double thresholdVal, ThresholdBitmask_traits) {
548  return (pixVal & static_cast<long>(thresholdVal));
549 }
550 
551 /*
552  * Advance the x_iterator to the variance image, when relevant (it may be NULL otherwise)
553  */
554 template<typename IterT>
555 static inline IterT
556 advancePtr(IterT varPtr, Threshold_traits) {
557  return varPtr;
558 }
559 
560 template<typename IterT>
561 static inline IterT
562 advancePtr(IterT varPtr, ThresholdPixelLevel_traits) {
563  return varPtr + 1;
564 }
565 
566 /*
567  * Here's the working routine for the FootprintSet constructors; see documentation
568  * of the constructors themselves
569  */
570 template<typename ImagePixelT, typename MaskPixelT, typename VariancePixelT, typename ThresholdTraitT>
571 static void findFootprints(
572  typename detection::FootprintSet::FootprintList *_footprints, // Footprints
573  geom::Box2I const& _region, // BBox of pixels that are being searched
574  image::ImageBase<ImagePixelT> const &img, // Image to search for objects
575  image::Image<VariancePixelT> const *var, // img's variance
576  double const footprintThreshold, // threshold value for footprint
577  double const includeThresholdMultiplier, // threshold (relative to footprintThreshold) for inclusion
578  bool const polarity, // if false, search _below_ thresholdVal
579  int const npixMin, // minimum number of pixels in an object
580  bool const setPeaks // should I set the Peaks list?
581 )
582 {
583  int id; /* object ID */
584  int in_span; /* object ID of current IdSpan */
585  int nobj = 0; /* number of objects found */
586  int x0 = 0; /* unpacked from a IdSpan */
587 
588  typedef typename image::Image<ImagePixelT> ImageT;
589  double includeThreshold = footprintThreshold * includeThresholdMultiplier; // Threshold for inclusion
590 
591  int const row0 = img.getY0();
592  int const col0 = img.getX0();
593  int const height = img.getHeight();
594  int const width = img.getWidth();
595 /*
596  * Storage for arrays that identify objects by ID. We want to be able to
597  * refer to idp[-1] and idp[width], hence the (width + 2)
598  */
599  std::vector<int> id1(width + 2);
600  std::fill(id1.begin(), id1.end(), 0);
601  std::vector<int> id2(width + 2);
602  std::fill(id2.begin(), id2.end(), 0);
603  std::vector<int>::iterator idc = id1.begin() + 1; // object IDs in current/
604  std::vector<int>::iterator idp = id2.begin() + 1; // previous row
605 
606  std::vector<int> aliases; // aliases for initially disjoint parts of Footprints
607  aliases.reserve(1 + height/20); // initial size of aliases
608 
609  std::vector<IdSpan::Ptr> spans; // y:x0,x1 for objects
610  spans.reserve(aliases.capacity()); // initial size of spans
611 
612  aliases.push_back(0); // 0 --> 0
613 /*
614  * Go through image identifying objects
615  */
616  typedef typename image::Image<ImagePixelT>::x_iterator x_iterator;
617  typedef typename image::Image<VariancePixelT>::x_iterator x_var_iterator;
618 
619  in_span = 0; // not in a span
620  for (int y = 0; y != height; ++y) {
621  if (idc == id1.begin() + 1) {
622  idc = id2.begin() + 1;
623  idp = id1.begin() + 1;
624  } else {
625  idc = id1.begin() + 1;
626  idp = id2.begin() + 1;
627  }
628  std::fill_n(idc - 1, width + 2, 0);
629 
630  in_span = 0; /* not in a span */
631  bool good = (includeThresholdMultiplier == 1.0); /* Span exceeds the threshold? */
632 
633  x_iterator pixPtr = img.row_begin(y);
634  x_var_iterator varPtr = (var == NULL) ? NULL : var->row_begin(y);
635  for (int x = 0; x < width; ++x, ++pixPtr, varPtr = advancePtr(varPtr, ThresholdTraitT())) {
636  ImagePixelT const pixVal = *pixPtr;
637 
638  if (isBadPixel(pixVal) ||
639  !inFootprint(pixVal, varPtr, polarity, footprintThreshold, ThresholdTraitT())) {
640  if (in_span) {
641  IdSpan::Ptr sp(new IdSpan(in_span, y, x0, x - 1, good));
642  spans.push_back(sp);
643 
644  in_span = 0;
645  good = false;
646  }
647  } else { /* a pixel to fix */
648  if (idc[x - 1] != 0) {
649  id = idc[x - 1];
650  } else if (idp[x - 1] != 0) {
651  id = idp[x - 1];
652  } else if (idp[x] != 0) {
653  id = idp[x];
654  } else if (idp[x + 1] != 0) {
655  id = idp[x + 1];
656  } else {
657  id = ++nobj;
658  aliases.push_back(id);
659  }
660 
661  idc[x] = id;
662  if (!in_span) {
663  x0 = x;
664  in_span = id;
665  }
666 /*
667  * Do we need to merge ID numbers? If so, make suitable entries in aliases[]
668  */
669  if (idp[x + 1] != 0 && idp[x + 1] != id) {
670  aliases[resolve_alias(aliases, idp[x + 1])] = resolve_alias(aliases, id);
671 
672  idc[x] = id = idp[x + 1];
673  }
674 
675  if (!good && inFootprint(pixVal, varPtr, polarity, includeThreshold, ThresholdTraitT())) {
676  good = true;
677  }
678  }
679  }
680 
681  if (in_span) {
682  IdSpan::Ptr sp(new IdSpan(in_span, y, x0, width - 1, good));
683  spans.push_back(sp);
684  }
685  }
686 /*
687  * Resolve aliases; first alias chains, then the IDs in the spans
688  */
689  for (unsigned int i = 0; i < spans.size(); i++) {
690  spans[i]->id = resolve_alias(aliases, spans[i]->id);
691  }
692 /*
693  * Sort spans by ID, so we can sweep through them once
694  */
695  if (spans.size() > 0) {
696  std::sort(spans.begin(), spans.end(), IdSpanCompar());
697  }
698 /*
699  * Build Footprints from spans
700  */
701  unsigned int i0; // initial value of i
702  if (spans.size() > 0) {
703  id = spans[0]->id;
704  i0 = 0;
705  for (unsigned int i = 0; i <= spans.size(); i++) { // <= size to catch the last object
706  if (i == spans.size() || spans[i]->id != id) {
707  PTR(detection::Footprint) fp(new detection::Footprint(i - i0, _region));
708 
709  bool good = false; // Span includes pixel sufficient to include footprint in set?
710  for (; i0 < i; i0++) {
711  good |= spans[i0]->good;
712  fp->addSpan(spans[i0]->y + row0, spans[i0]->x0 + col0, spans[i0]->x1 + col0);
713  }
714 
715  if (good && !(fp->getNpix() < npixMin)) {
716  _footprints->push_back(fp);
717  }
718  }
719 
720  if (i < spans.size()) {
721  id = spans[i]->id;
722  }
723  }
724  }
725 /*
726  * Find all peaks within those Footprints
727  */
728  if (setPeaks) {
729  typedef detection::FootprintSet::FootprintList::iterator fiterator;
730  for (fiterator ptr = _footprints->begin(), end = _footprints->end(); ptr != end; ++ptr) {
731  findPeaks(*ptr, img, polarity, ThresholdTraitT());
732  }
733  }
734 }
735 
736 /************************************************************************************************************/
737 /*
738  * \brief Find a FootprintSet given an Image and a threshold
739  */
740 template<typename ImagePixelT>
742  image::Image<ImagePixelT> const &img,
743  Threshold const &threshold,
744  int const npixMin,
745  bool const setPeaks
746 ) : lsst::daf::base::Citizen(typeid(this)),
747  _footprints(new FootprintList()),
748  _region(img.getBBox())
749 {
750  typedef float VariancePixelT;
751 
752  findFootprints<ImagePixelT, afw::image::MaskPixel, VariancePixelT, ThresholdLevel_traits>(
753  _footprints.get(),
754  _region,
755  img,
756  NULL,
757  threshold.getValue(img), threshold.getIncludeMultiplier(), threshold.getPolarity(),
758  npixMin,
759  setPeaks
760  );
761 }
762 
763 // NOTE: not a template to appease swig (see note by instantiations at bottom)
764 
765 /*
766  * \brief Find a FootprintSet given a Mask and a threshold
767  */
768 template <typename MaskPixelT>
770  image::Mask<MaskPixelT> const &msk,
771  Threshold const &threshold,
772  int const npixMin
773 ) : lsst::daf::base::Citizen(typeid(this)),
774  _footprints(new FootprintList()),
775  _region(msk.getBBox())
776 {
777  switch (threshold.getType()) {
778  case Threshold::BITMASK:
779  findFootprints<MaskPixelT, MaskPixelT, float, ThresholdBitmask_traits>(
780  _footprints.get(), _region, msk, NULL, threshold.getValue(), threshold.getIncludeMultiplier(),
781  threshold.getPolarity(), npixMin, false);
782  break;
783 
784  case Threshold::VALUE:
785  findFootprints<MaskPixelT, MaskPixelT, float, ThresholdLevel_traits>(
786  _footprints.get(), _region, msk, NULL, threshold.getValue(), threshold.getIncludeMultiplier(),
787  threshold.getPolarity(), npixMin, false);
788  break;
789 
790  default:
791  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
792  "You must specify a numerical threshold value with a Mask");
793  }
794 }
795 
796 
809 template<typename ImagePixelT, typename MaskPixelT>
812  Threshold const &threshold,
813  std::string const &planeName,
814  int const npixMin,
815  bool const setPeaks
816 ) : lsst::daf::base::Citizen(typeid(this)),
817  _footprints(new FootprintList()),
818  _region(
819  geom::Point2I(maskedImg.getX0(), maskedImg.getY0()),
820  geom::Extent2I(maskedImg.getWidth(), maskedImg.getHeight())
821  )
822 {
823  typedef typename image::MaskedImage<ImagePixelT, MaskPixelT>::Variance::Pixel VariancePixelT;
824  // Find the Footprints
825  switch (threshold.getType()) {
827  findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdPixelLevel_traits>(
828  _footprints.get(),
829  _region,
830  *maskedImg.getImage(),
831  maskedImg.getVariance().get(),
832  threshold.getValue(maskedImg),
833  threshold.getIncludeMultiplier(),
834  threshold.getPolarity(),
835  npixMin,
836  setPeaks
837  );
838  break;
839  default:
840  findFootprints<ImagePixelT, MaskPixelT, VariancePixelT, ThresholdLevel_traits>(
841  _footprints.get(),
842  _region,
843  *maskedImg.getImage(),
844  maskedImg.getVariance().get(),
845  threshold.getValue(maskedImg),
846  threshold.getIncludeMultiplier(),
847  threshold.getPolarity(),
848  npixMin,
849  setPeaks
850  );
851  break;
852  }
853  // Set Mask if requested
854  if (planeName == "") {
855  return;
856  }
857  //
858  // Define the maskPlane
859  //
860  const typename image::Mask<MaskPixelT>::Ptr mask = maskedImg.getMask();
861  mask->addMaskPlane(planeName);
862 
863  MaskPixelT const bitPlane = mask->getPlaneBitMask(planeName);
864  //
865  // Set the bits where objects are detected
866  //
867  typedef image::Mask<MaskPixelT> MaskT;
868 
869  class MaskFootprint : public detection::FootprintFunctor<MaskT> {
870  public:
871  MaskFootprint(MaskT const& mimage,
872  MaskPixelT bit) : detection::FootprintFunctor<MaskT>(mimage), _bit(bit) {}
873 
874  void operator()(typename MaskT::xy_locator loc, int, int) {
875  *loc |= _bit;
876  }
877  private:
878  MaskPixelT _bit;
879  };
880 
881  MaskFootprint maskit(*maskedImg.getMask(), bitPlane);
882  for (FootprintList::const_iterator fiter = _footprints->begin();
883  fiter != _footprints->end(); ++fiter
884  ) {
885  Footprint::Ptr const foot = *fiter;
886 
887  maskit.apply(*foot);
888  }
889 }
890 
891 /************************************************************************************************************/
897 template <typename ImagePixelT, typename MaskPixelT>
900  Threshold const &,
901  int,
902  int,
903  std::vector<PTR(Peak)> const *
904 ) : lsst::daf::base::Citizen(typeid(this)),
905  _footprints(new FootprintList()),
906  _region(geom::Point2I(img.getX0(), img.getY0()),
907  geom::Extent2I(img.getWidth(), img.getHeight()))
908 {
909  throw LSST_EXCEPT(lsst::pex::exceptions::LogicError, "NOT IMPLEMENTED");
910 }
911 
912 
913 /************************************************************************************************************/
914 namespace {
916  /*
917  * A data structure to hold the starting point for a search for pixels above threshold,
918  * used by pmFindFootprintAtPoint
919  *
920  * We don't want to find this span again --- it's already part of the footprint ---
921  * so we set appropriate mask bits
922  */
923  //
924  // An enum for what we should do with a Startspan
925  //
926  enum DIRECTION {DOWN = 0, // scan down from this span
927  UP, // scan up from this span
928  RESTART, // restart scanning from this span
929  DONE // this span is processed
930  };
931  //
932  // A Class that remembers how to [re-]start scanning the image for pixels
933  //
934  template<typename MaskPixelT>
935  class Startspan {
936  public:
937  typedef std::vector<boost::shared_ptr<Startspan> > Ptr;
938 
939  Startspan(detection::Span const *span, image::Mask<MaskPixelT> *mask, DIRECTION const dir);
940  ~Startspan() { delete _span; }
941 
942  bool getSpan() { return _span; }
943  bool Stop() { return _stop; }
944  DIRECTION getDirection() { return _direction; }
945 
946  static int detectedPlane; // The MaskPlane to use for detected pixels
947  static int stopPlane; // The MaskPlane to use for pixels that signal us to stop searching
948  private:
949  detection::Span::Ptr const _span; // The initial Span
950  DIRECTION _direction; // How to continue searching for further pixels
951  bool _stop; // should we stop searching?
952  };
953 
954  template<typename MaskPixelT>
955  Startspan<MaskPixelT>::Startspan(detection::Span const *span, // The span in question
956  image::Mask<MaskPixelT> *mask, // Pixels that we've already detected
957  DIRECTION const dir // Should we continue searching towards the top of the image?
958  ) :
959  _span(span),
960  _direction(dir),
961  _stop(false) {
962 
963  if (mask != NULL) { // remember that we've detected these pixels
964  mask->setMaskPlaneValues(detectedPlane, span->getX0(), span->getX1(), span->getY());
965 
966  int const y = span->getY() - mask->getY0();
967  for (int x = span->getX0() - mask->getX0(); x <= span->getX1() - mask->getX0(); x++) {
968  if (mask(x, y, stopPlane)) {
969  _stop = true;
970  break;
971  }
972  }
973  }
974  }
975 
976  template<typename ImagePixelT, typename MaskPixelT>
977  class StartspanSet {
978  public:
980  _image(image->getImage()),
981  _mask(image->getMask()) {}
982  ~StartspanSet() {}
983 
984  bool add(detection::Span *span, DIRECTION const dir, bool addToMask = true);
985  bool process(detection::Footprint *fp, // the footprint that we're building
986  detection::Threshold const &threshold, // Threshold
987  double const param = -1); // parameter that Threshold may need
988  private:
989  image::Image<ImagePixelT> const *_image; // the Image we're searching
990  image::Mask<MaskPixelT> *_mask; // the mask that tells us where we've got to
991  std::vector<typename Startspan<MaskPixelT>::Ptr> _spans; // list of Startspans
992  };
993 
994  //
995  // Add a new Startspan to a StartspansSet. Iff we see a stop bit, return true
996  //
997  template<typename ImagePixelT, typename MaskPixelT>
998  bool StartspanSet<ImagePixelT, MaskPixelT>::add(detection::Span *span, // the span in question
999  DIRECTION const dir, // the desired direction to search
1000  bool addToMask) { // should I add the Span to the mask?
1001  if (dir == RESTART) {
1002  if (add(span, UP) || add(span, DOWN, false)) {
1003  return true;
1004  }
1005  } else {
1006  typename Startspan<MaskPixelT>::Ptr sspan(new Startspan<MaskPixelT>(span, dir));
1007  if (sspan->stop()) { // we detected a stop bit
1008  return true;
1009  } else {
1010  _spans.push_back(sspan);
1011  }
1012  }
1013 
1014  return false;
1015  }
1016 
1017  /************************************************************************************************/
1018  /*
1019  * Search the image for pixels above threshold, starting at a single Startspan.
1020  * We search the array looking for one to process; it'd be better to move the
1021  * ones that we're done with to the end, but it probably isn't worth it for
1022  * the anticipated uses of this routine.
1023  *
1024  * This is the guts of pmFindFootprintAtPoint
1025  */
1026  template<typename ImagePixelT, typename MaskPixelT>
1027  bool StartspanSet<ImagePixelT, MaskPixelT>::process(
1028  detection::Footprint *fp, // the footprint that we're building
1029  detection::Threshold const &threshold, // Threshold
1030  double const param // parameter that Threshold may need
1031  ) {
1032  int const row0 = _image->getY0();
1033  int const col0 = _image->getOffsetCols();
1034  int const height = _image->getHeight();
1035 
1036  /**********************************************************************************************/
1037 
1038  Startspan<MaskPixelT> *sspan = NULL;
1039  for (auto iter = _spans.begin(); iter != _spans.end(); iter++) {
1040  *sspan = *iter;
1041  if (sspan->getDirection() != DONE) {
1042  break;
1043  }
1044  if (sspan->Stop()) {
1045  break;
1046  }
1047  }
1048  if (sspan == NULL || sspan->getDirection() == DONE) { // no more Startspans to process
1049  return false;
1050  }
1051  if (sspan->Stop()) { // they don't want any more spans processed
1052  return false;
1053  }
1054  /*
1055  * Work
1056  */
1057  DIRECTION const dir = sspan->getDirection();
1058  /*
1059  * Set initial span to the startspan
1060  */
1061  int x0 = sspan->getSpan()->getX0() - col0;
1062  /*
1063  * Go through image identifying objects
1064  */
1065  int nx0 = -1; // new value of x0
1066  int const di = (dir == UP) ? 1 : -1; // how much i changes to get to the next row
1067  bool stop = false; // should I stop searching for spans?
1068 
1069  typedef typename image::Image<ImagePixelT>::pixel_accessor pixAccessT;
1070  double const thresholdVal = threshold.getValue(param);
1071  bool const polarity = threshold.getPolarity();
1072 
1073  for (int i = sspan->span->y -row0 + di; i < height && i >= 0; i += di) {
1074  pixAccessT imgRow = _image->origin().advance(0, i); // row pointer
1075  //maskPixAccessT maskRow = _mask->origin.advance(0, i); // masks's row pointer
1076  //
1077  // Search left from the pixel diagonally to the left of (i - di, x0). If there's
1078  // a connected span there it may need to grow up and/or down, so push it onto
1079  // the stack for later consideration
1080  //
1081  nx0 = -1;
1082  for (int j = x0 - 1; j >= -1; j--) {
1083  ImagePixelT pixVal = (j < 0) ? thresholdVal - 100 : (polarity ? imgRow[j] : -imgRow[j]);
1084  if (_mask(j, i, Startspan<MaskPixelT>::detectedPlane) || pixVal < threshold) {
1085  if (j < x0 - 1) { // we found some pixels above threshold
1086  nx0 = j + 1;
1087  }
1088  break;
1089  }
1090  }
1091 #if 0
1092  if (nx0 < 0) { // no span to the left
1093  nx1 = x0 - 1; // we're going to resume searching at nx1 + 1
1094  } else {
1095  //
1096  // Search right in leftmost span
1097  //
1098  //nx1 = 0; // make gcc happy
1099  for (int j = nx0 + 1; j <= width; j++) {
1100  ImagePixelT pixVal = (j >= width) ? threshold - 100 :
1101  (polarity ? (F32 ? imgRowF32[j] : imgRowS32[j]) :
1102  (F32 ? -imgRowF32[j] : -imgRowS32[j]));
1103  if ((maskRow[j] & DETECTED) || pixVal < threshold) {
1104  nx1 = j - 1;
1105  break;
1106  }
1107  }
1108 
1109  pmSpan const *sp = pmFootprintAddSpan(fp, i + row0, nx0 + col0, nx1 + col0);
1110 
1111  if (add_startspan(startspans, sp, mask, RESTART)) {
1112  stop = true;
1113  break;
1114  }
1115  }
1116  //
1117  // Now look for spans connected to the old span. The first of these we'll
1118  // simply process, but others will have to be deferred for later consideration.
1119  //
1120  // In fact, if the span overhangs to the right we'll have to defer the overhang
1121  // until later too, as it too can grow in both directions
1122  //
1123  // Note that column width exists virtually, and always ends the last span; this
1124  // is why we claim below that sx1 is always set
1125  //
1126  bool first = false; // is this the first new span detected?
1127  for (int j = nx1 + 1; j <= x1 + 1; j++) {
1128  ImagePixelT pixVal = (j >= width) ? threshold - 100 :
1129  (polarity ? (F32 ? imgRowF32[j] : imgRowS32[j]) : (F32 ? -imgRowF32[j] : -imgRowS32[j]));
1130  if (!(maskRow[j] & DETECTED) && pixVal >= threshold) {
1131  int sx0 = j++; // span that we're working on is sx0:sx1
1132  int sx1 = -1; // We know that if we got here, we'll also set sx1
1133  for (; j <= width; j++) {
1134  ImagePixelT pixVal = (j >= width) ? threshold - 100 :
1135  (polarity ? (F32 ? imgRowF32[j] : imgRowS32[j]) :
1136  (F32 ? -imgRowF32[j] : -imgRowS32[j]));
1137  if ((maskRow[j] & DETECTED) || pixVal < threshold) { // end of span
1138  sx1 = j;
1139  break;
1140  }
1141  }
1142  assert (sx1 >= 0);
1143 
1144  pmSpan const *sp;
1145  if (first) {
1146  if (sx1 <= x1) {
1147  sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
1148  if (add_startspan(startspans, sp, mask, DONE)) {
1149  stop = true;
1150  break;
1151  }
1152  } else { // overhangs to right
1153  sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, x1 + col0);
1154  if (add_startspan(startspans, sp, mask, DONE)) {
1155  stop = true;
1156  break;
1157  }
1158  sp = pmFootprintAddSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);
1159  if (add_startspan(startspans, sp, mask, RESTART)) {
1160  stop = true;
1161  break;
1162  }
1163  }
1164  first = false;
1165  } else {
1166  sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
1167  if (add_startspan(startspans, sp, mask, RESTART)) {
1168  stop = true;
1169  break;
1170  }
1171  }
1172  }
1173  }
1174 
1175  if (stop || first == false) { // we're done
1176  break;
1177  }
1178 
1179  x0 = nx0;
1180  x1 = nx1;
1181 #endif
1182  }
1183  /*
1184  * Cleanup
1185  */
1186 
1187  sspan->_direction = DONE;
1188  return stop ? false : true;
1189  }
1191 }
1192 #if 0
1193 
1194 
1195 /*
1196  * Go through an image, starting at (row, col) and assembling all the pixels
1197  * that are connected to that point (in a chess kings-move sort of way) into
1198  * a pmFootprint.
1199  *
1200  * This is much slower than pmFindFootprints if you want to find lots of
1201  * footprints, but if you only want a small region about a given point it
1202  * can be much faster
1203  *
1204  * N.b. The returned pmFootprint is not in "normal form"; that is the pmSpans
1205  * are not sorted by increasing y, x0, x1. If this matters to you, call
1206  * pmFootprintNormalize()
1207  */
1208 pmFootprint *
1209 pmFindFootprintAtPoint(psImage const *img, // image to search
1210  Threshold const &threshold, // Threshold
1211  psArray const *peaks, // array of peaks; finding one terminates search for footprint
1212  int row, int col) { // starting position (in img's parent's coordinate system)
1213  assert(img != NULL);
1214 
1215  bool F32 = false; // is this an F32 image?
1216  if (img->type.type == PS_TYPE_F32) {
1217  F32 = true;
1218  } else if (img->type.type == PS_TYPE_S32) {
1219  F32 = false;
1220  } else { // N.b. You can't trivially add more cases here; F32 is just a bool
1221  psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
1222  return NULL;
1223  }
1224  psF32 *imgRowF32 = NULL; // row pointer if F32
1225  psS32 *imgRowS32 = NULL; // " " " " !F32
1226 
1227  int const row0 = img->row0;
1228  int const col0 = img->col0;
1229  int const height = img->getHeight();
1230  int const width = img->getWidth();
1231 /*
1232  * Is point in image, and above threshold?
1233  */
1234  row -= row0;
1235  col -= col0;
1236  if (row < 0 || row >= height ||
1237  col < 0 || col >= width) {
1238  psError(PS_ERR_BAD_PARAMETER_VALUE, true,
1239  "row/col == (%d, %d) are out of bounds [%d--%d, %d--%d]",
1240  row + row0, col + col0, row0, row0 + height - 1, col0, col0 + width - 1);
1241  return NULL;
1242  }
1243 
1244  ImagePixelT pixVal = F32 ? img->data.F32[row][col] : img->data.S32[row][col];
1245  if (pixVal < threshold) {
1246  return pmFootprintAlloc(0, img);
1247  }
1248 
1249  pmFootprint *fp = pmFootprintAlloc(1 + img->getHeight()/10, img);
1250 /*
1251  * We need a mask for two purposes; to indicate which pixels are already detected,
1252  * and to store the "stop" pixels --- those that, once reached, should stop us
1253  * looking for the rest of the pmFootprint. These are generally set from peaks.
1254  */
1255  psImage *mask = psImageAlloc(width, height, PS_TYPE_MASK);
1256  P_PSIMAGE_SET_ROW0(mask, row0);
1257  P_PSIMAGE_SET_COL0(mask, col0);
1258  psImageInit(mask, INITIAL);
1259  //
1260  // Set stop bits from peaks list
1261  //
1262  assert (peaks == NULL || peaks->n == 0 || pmIsPeak(peaks->data[0]));
1263  if (peaks != NULL) {
1264  for (int i = 0; i < peaks->n; i++) {
1265  pmPeak *peak = peaks->data[i];
1266  mask->data.PS_TYPE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= STOP;
1267  }
1268  }
1269 /*
1270  * Find starting span passing through (row, col)
1271  */
1272  psArray *startspans = psArrayAllocEmpty(1); // spans where we have to restart the search
1273 
1274  imgRowF32 = img->data.F32[row]; // only one of
1275  imgRowS32 = img->data.S32[row]; // these is valid!
1276  psMaskType *maskRow = mask->data.PS_TYPE_MASK_DATA[row];
1277  {
1278  int i;
1279  for (i = col; i >= 0; i--) {
1280  pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
1281  if ((maskRow[i] & DETECTED) || pixVal < threshold) {
1282  break;
1283  }
1284  }
1285  int i0 = i;
1286  for (i = col; i < width; i++) {
1287  pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
1288  if ((maskRow[i] & DETECTED) || pixVal < threshold) {
1289  break;
1290  }
1291  }
1292  int i1 = i;
1293  pmSpan const *sp = pmFootprintAddSpan(fp, row + row0, i0 + col0 + 1, i1 + col0 - 1);
1294 
1295  (void)add_startspan(startspans, sp, mask, RESTART);
1296  }
1297  /*
1298  * Now workout from those Startspans, searching for pixels above threshold
1299  */
1300  while (do_startspan(fp, img, mask, threshold, startspans)) continue;
1301  /*
1302  * Cleanup
1303  */
1304  psFree(mask);
1305  psFree(startspans); // restores the image pixel
1306 
1307  return fp; // pmFootprint really
1308 }
1309 #endif
1310 
1311 /************************************************************************************************************/
1316 ) :
1317  lsst::daf::base::Citizen(typeid(this)),
1318  _footprints(PTR(FootprintList)(new FootprintList)), _region(region) {
1319 }
1320 
1325  FootprintSet const &rhs
1326 ) :
1327  lsst::daf::base::Citizen(typeid(this)),
1328  _footprints(new FootprintList), _region(rhs._region)
1329 {
1330  _footprints->reserve(rhs._footprints->size());
1331  for (FootprintSet::FootprintList::const_iterator ptr = rhs._footprints->begin(),
1332  end = rhs._footprints->end(); ptr != end; ++ptr) {
1333  _footprints->push_back(PTR(Footprint)(new Footprint(**ptr)));
1334  }
1335 }
1336 
1340  FootprintSet tmp(rhs);
1341  swap(tmp); // See Meyers, Effective C++, Item 11
1342  return *this;
1343 }
1344 
1345 /************************************************************************************************************/
1350  detection::FootprintSet const& rhs,
1351  int tGrow,
1352  int rGrow,
1353  bool isotropic
1354 )
1355 {
1356  detection::FootprintControl const ctrl(true, isotropic);
1357  detection::FootprintSet fs = mergeFootprintSets(*this, tGrow, rhs, rGrow, ctrl);
1358  swap(fs); // Swap the new FootprintSet into place
1359 }
1360 
1364 //
1366  geom::Box2I const& region
1367 ) {
1368  _region = region;
1369 
1370  for (FootprintSet::FootprintList::iterator ptr = _footprints->begin(),
1371  end = _footprints->end(); ptr != end; ++ptr
1372  ) {
1373  (*ptr)->setRegion(region);
1374  }
1375 }
1376 
1377 /************************************************************************************************************/
1384  FootprintSet const &rhs,
1385  int r,
1386  bool isotropic
1387 )
1389  : lsst::daf::base::Citizen(typeid(this)), _footprints(new FootprintList), _region(rhs._region)
1390 {
1391  if (r == 0) {
1392  FootprintSet fs = rhs;
1393  swap(fs); // Swap the new FootprintSet into place
1394  return;
1395  } else if (r < 0) {
1396  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
1397  (boost::format("I cannot grow by negative numbers: %d") % r).str());
1398  }
1399 
1400  detection::FootprintControl const ctrl(true, isotropic);
1401  detection::FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, r, ctrl);
1402  swap(fs); // Swap the new FootprintSet into place
1403 }
1404 
1405 /************************************************************************************************************/
1406 
1408  int ngrow,
1409  detection::FootprintControl const& ctrl)
1410  : lsst::daf::base::Citizen(typeid(this)), _footprints(new FootprintList), _region(rhs._region)
1411 {
1412  if (ngrow == 0) {
1413  FootprintSet fs = rhs;
1414  swap(fs); // Swap the new FootprintSet into place
1415  return;
1416  } else if (ngrow < 0) {
1417  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
1418  str(boost::format("I cannot grow by negative numbers: %d") % ngrow));
1419  }
1420 
1421  detection::FootprintSet fs = mergeFootprintSets(FootprintSet(rhs.getRegion()), 0, rhs, ngrow, ctrl);
1422  swap(fs); // Swap the new FootprintSet into place
1423 }
1424 
1425 /************************************************************************************************************/
1432  FootprintSet const& fs1,
1433  FootprintSet const& fs2,
1434  bool const
1435  )
1436  : lsst::daf::base::Citizen(typeid(this)),
1437  _footprints(new FootprintList()),
1438  _region(fs1._region)
1439 {
1440  _region.include(fs2._region);
1441  throw LSST_EXCEPT(lsst::pex::exceptions::LogicError, "NOT IMPLEMENTED");
1442 }
1443 
1444 /************************************************************************************************************/
1451 detection::FootprintSet::insertIntoImage(
1452  bool const relativeIDs
1453 ) const {
1456  );
1457  *im = 0;
1458 
1460  for (FootprintList::const_iterator fiter = _footprints->begin();
1461  fiter != _footprints->end(); fiter++
1462  ) {
1463  Footprint::Ptr const foot = *fiter;
1464 
1465  if (relativeIDs) {
1466  id++;
1467  } else {
1468  id = foot->getId();
1469  }
1470 
1471  foot->insertIntoImage(*im.get(), id);
1472  }
1473 
1474  return im;
1475 }
1476 
1477 /************************************************************************************************************/
1481 template<typename ImagePixelT, typename MaskPixelT>
1482 void
1485  HeavyFootprintCtrl const *ctrl
1486 )
1487 {
1489 
1490  if (!ctrl) {
1491  ctrl = &ctrl_s;
1492  }
1493 
1494  for (FootprintList::iterator ptr = _footprints->begin(),
1495  end = _footprints->end(); ptr != end; ++ptr) {
1496  ptr->reset(new detection::HeavyFootprint<ImagePixelT, MaskPixelT>(**ptr, mimg, ctrl));
1497  }
1498 }
1499 
1502 ) const {
1503  for (FootprintList::const_iterator i = _footprints->begin(); i != _footprints->end(); ++i) {
1504  PTR(afw::table::SourceRecord) r = cat.addNew();
1505  r->setFootprint(*i);
1506  }
1507 }
1508 
1509 
1510 /************************************************************************************************************/
1511 //
1512 // Explicit instantiations
1513 //
1514 
1515 #ifndef DOXYGEN
1516 
1517 #define INSTANTIATE(PIXEL) \
1518  template detection::FootprintSet::FootprintSet( \
1519  image::Image<PIXEL> const &, Threshold const &, int const, bool const); \
1520  template detection::FootprintSet::FootprintSet( \
1521  image::MaskedImage<PIXEL,image::MaskPixel> const &, Threshold const &, \
1522  std::string const &, int const, bool const);\
1523  template detection::FootprintSet::FootprintSet( \
1524  image::MaskedImage<PIXEL,image::MaskPixel> const &, Threshold const &, \
1525  int, int, std::vector<PTR(Peak)> const *); \
1526  template void detection::FootprintSet::makeHeavy(image::MaskedImage<PIXEL,image::MaskPixel> const &, \
1527  HeavyFootprintCtrl const *)
1528 
1530  Threshold const &, int const);
1531 
1532 template void detection::FootprintSet::setMask(image::Mask<image::MaskPixel> *, std::string const &);
1533 template void detection::FootprintSet::setMask(PTR(image::Mask<image::MaskPixel>), std::string const &);
1534 
1535 INSTANTIATE(boost::uint16_t);
1536 INSTANTIATE(int);
1537 INSTANTIATE(float);
1538 INSTANTIATE(double);
1539 
1540 #endif // !DOXYGEN
int y
int iter
#define CONST_PTR(...)
Definition: base.h:47
ImagePtr getImage(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage&#39;s image.
Definition: MaskedImage.h:869
A Control Object for Footprints, controlling e.g. how they are grown.
Definition: FootprintCtrl.h:36
x_iterator row_begin(int y) const
Definition: Image.h:319
for(FootprintList::const_iterator ptr=feet->begin(), end=feet->end();ptr!=end;++ptr)
Definition: saturated.cc:82
void swap(ImageBase< PixelT > &a, ImageBase< PixelT > &b)
Definition: Image.cc:291
std::vector< Peak::Ptr > PeakList
Definition: Footprint.h:80
A control object for HeavyFootprints.
Definition: FootprintCtrl.h:91
std::pair< bool, bool > isIsotropic() const
Return &lt;isSet, Value&gt; for isotropic grows.
Definition: FootprintCtrl.h:77
geom::Box2I const getRegion() const
Definition: FootprintSet.h:195
lsst::afw::detection::Footprint Footprint
Definition: Source.h:61
definition of the Trace messaging facilities
Use number of sigma given per-pixel s.d.
Definition: Threshold.h:52
Represent a set of pixels of an arbitrary shape and size, including values for those pixels; a HeavyF...
std::vector< Footprint::Ptr > FootprintList
The FootprintSet&#39;s set of Footprints.
Definition: FootprintSet.h:57
int getMinY() const
Definition: Box.h:125
A Threshold is used to pass a threshold value to detection algorithms.
Definition: Threshold.h:44
boost::shared_ptr< Footprint > Ptr
Definition: Footprint.h:75
#define PTR(...)
Definition: base.h:41
Point< int, 2 > Point2I
Definition: PSF.h:39
iterator begin() const
Definition: Image.cc:325
The base class for all image classed (Image, Mask, MaskedImage, ...)
Definition: Image.h:115
static int addMaskPlane(const std::string &name)
Definition: Mask.cc:699
int isnan(T t)
Definition: ieee.h:110
void setMaskPlaneValues(const int plane, const int x0, const int x1, const int y)
Set the bit specified by &quot;planeId&quot; for pixels (x0, y) ... (x1, y)
Definition: Mask.cc:1141
int const x0
Definition: saturated.cc:45
PixelT Pixel
A pixel in this ImageBase.
Definition: Image.h:133
boost::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
Definition: Catalog.h:469
boost::shared_ptr< Mask > Ptr
Definition: Mask.h:95
An integer coordinate rectangle.
Definition: Box.h:53
Vector param
A set of pixels in an Image, including those pixels&#39; actual values.
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
Represent a collections of footprints associated with image data.
VariancePtr getVariance(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage&#39;s variance.
Definition: MaskedImage.h:890
void merge(FootprintSet const &rhs, int tGrow=0, int rGrow=0, bool isotropic=true)
boost::uint64_t FootprintIdPixel
Definition: FootprintSet.h:46
std::pair< bool, bool > isLeft() const
Definition: FootprintCtrl.h:65
float getPeakValue() const
Return the value of the image at the peak.
Definition: Peak.h:114
std::pair< bool, bool > isCircular() const
Definition: FootprintCtrl.h:63
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:237
double max
Definition: attributes.cc:218
boost::shared_ptr< Footprint > growFootprint(Footprint const &foot, int nGrow, bool isotropic=true)
A peak in an image.
Definition: Peak.h:51
Use (pixels &amp; (given mask))
Definition: Threshold.h:49
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
Return the bitmask corresponding to a vector of plane names OR&#39;d together.
Definition: Mask.cc:859
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55
void setMask(image::Mask< MaskPixelT > *mask, std::string const &planeName)
Definition: FootprintSet.h:202
bool getPolarity() const
return Threshold&#39;s polarity
Definition: Threshold.h:88
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:93
#define INSTANTIATE(TYPE)
Definition: ImagePca.cc:128
int getIx() const
Return the column pixel position.
Definition: Peak.h:98
double _y
Definition: ImageUtils.cc:328
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:77
int getIy() const
Definition: Peak.h:99
if(width!=gim.getWidth()||height!=gim.getHeight()||x0!=gim.getX0()||y0!=gim.getY0())
Definition: saturated.cc:47
std::pair< bool, bool > isUp() const
Definition: FootprintCtrl.h:67
detection::FootprintSet::FootprintList FootprintList
Definition: saturated.cc:80
int getMinX() const
Definition: Box.h:124
A set of pixels in an Image.
Definition: Footprint.h:70
int getY0() const
Definition: Image.h:255
void setRegion(geom::Box2I const &region)
int x
double getIncludeMultiplier() const
return includeMultiplier
Definition: Threshold.h:94
Support for peaks in images.
int row
Definition: CR.cc:153
double getValue(const double param=-1) const
Definition: Threshold.cc:79
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
A set of Footprints, associated with a MaskedImage.
Definition: FootprintSet.h:53
std::pair< bool, bool > isRight() const
Definition: FootprintCtrl.h:66
Control Footprint-related algorithms.
MaskPtr getMask(bool const noThrow=false) const
Return a (Ptr to) the MaskedImage&#39;s mask.
Definition: MaskedImage.h:879
geom::Box2I _region
The corners of the MaskedImage that the detections live in.
Definition: FootprintSet.h:229
virtual void operator()(typename ImageT::xy_locator loc, int x, int y)=0
int id
Definition: CR.cc:151
int resolve_alias(const std::vector< int > &aliases, int id)
Definition: CR.cc:92
Compute Image Statistics.
afw::table::Key< double > b
void makeSources(afw::table::SourceCatalog &catalog) const
Add a new record corresponding to each footprint to a SourceCatalog.
Record class that contains measurements made on a single exposure.
Definition: Source.h:81
Implementation of the Class MaskedImage.
int getHeight() const
Return the number of rows in the image.
Definition: Image.h:239
std::pair< bool, bool > isDown() const
Definition: FootprintCtrl.h:68
FootprintSet(image::Image< ImagePixelT > const &img, Threshold const &threshold, int const npixMin=1, bool const setPeaks=true)
int const npixMin
Definition: saturated.cc:72
void makeHeavy(image::MaskedImage< ImagePixelT, MaskPixelT > const &mimg, HeavyFootprintCtrl const *ctrl=NULL)
int getId() const
Return the Peak&#39;s unique ID.
Definition: Peak.h:96
void swap(FootprintSet &rhs)
Definition: FootprintSet.h:157
ThresholdType getType() const
return type of threshold
Definition: Threshold.h:67
A functor class to allow users to process all the pixels in a Footprint.
FootprintSet & operator=(FootprintSet const &rhs)
Assignment operator.
ImageT val
Definition: CR.cc:154
A class to represent a 2-dimensional array of pixels.
Definition: PSF.h:43
boost::shared_ptr< FootprintList > _footprints
the Footprints of detected objects
Definition: FootprintSet.h:228
Extent< int, 2 > Extent2I
Definition: Extent.h:355
Include files required for standard LSST Exception handling.
int col
Definition: CR.cc:152
boost::shared_ptr< FootprintList > getFootprints()
Definition: FootprintSet.h:168
int getX0() const
Definition: Image.h:247