LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
FootprintMerge.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2008-2014 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 #include "boost/bind.hpp"
23 
27 
28 namespace lsst { namespace afw { namespace detection {
29 
30 namespace {
31 
32 FootprintSet mergeFootprintPair(Footprint const &foot1, Footprint const &foot2) {
33 
34  geom::Box2I bbox(foot1.getBBox());
35  bbox.include(foot2.getBBox());
36 
37  boost::uint16_t bits = 0x1;
39  setMaskFromFootprint(&mask, foot1, bits);
40  setMaskFromFootprint(&mask, foot2, bits);
41  FootprintSet fpSet(mask, Threshold(bits, Threshold::BITMASK));
42  return fpSet;
43 }
44 
45 } // anonymous namespace
46 
48 public:
49 
52 
53  explicit FootprintMerge(
54  PTR(Footprint) footprint,
55  PTR(afw::table::SourceTable) sourceTable,
56  PTR(PeakTable) peakTable,
57  afw::table::SchemaMapper const & peakSchemaMapper,
58  KeyTuple const & keys
59  ) :
60  _footprints(1, footprint),
61  _source(sourceTable->makeRecord())
62  {
63  PTR(Footprint) newFootprint = boost::make_shared<Footprint>(*footprint);
64 
65  _source->set(keys.footprint, true);
66  // Replace all the Peaks in the merged Footprint with new ones that include the origin flags
67  newFootprint->getPeaks() = PeakCatalog(peakTable);
68  for (
69  PeakCatalog::iterator iter = footprint->getPeaks().begin();
70  iter != footprint->getPeaks().end();
71  ++iter
72  ) {
73  PTR(PeakRecord) newPeak = peakTable->copyRecord(*iter, peakSchemaMapper);
74  newPeak->set(keys.peak, true);
75  newFootprint->getPeaks().push_back(newPeak);
76  }
77  _source->setFootprint(newFootprint);
78  }
79 
80  /*
81  * Does this Footprint overlap the merged Footprint.
82  *
83  * The current implementation just builds an image from the two Footprints and
84  * detects the number of peaks. This is not very efficient and will be changed
85  * within the Footprint class in the future.
86  */
87  bool overlaps(Footprint const &rhs) const {
88  return mergeFootprintPair(*getMergedFootprint(), rhs).getFootprints()->size() == 1u;
89  }
90 
91  /*
92  * Add this Footprint to the merge.
93  *
94  * If minNewPeakDist >= 0, it will add all peaks from foot to the merged Footprint
95  * that are greater than minNewPeakDist away from the closest existing peak.
96  * If minNewPeakDist < 0, no peaks will be added from foot.
97  *
98  * If foot does not overlap it will do nothing.
99  */
100  void add(
101  PTR(Footprint) footprint,
102  afw::table::SchemaMapper const & peakSchemaMapper,
103  KeyTuple const & keys,
104  float minNewPeakDist=-1.,
105  float maxSamePeakDist=-1.
106  ) {
107  if (_addSpans(footprint)) {
108  _footprints.push_back(footprint);
109  _source->set(keys.footprint, true);
110  _addPeaks(footprint->getPeaks(), &peakSchemaMapper, &keys, minNewPeakDist, maxSamePeakDist);
111  }
112  }
113 
114  /*
115  * Merge an already-merged clump of Footprints into this
116  *
117  * If minNewPeakDist >= 0, it will add all peaks from foot to the merged Footprint
118  * that are greater than minNewPeakDist away from the closest existing peak.
119  * If minNewPeakDist < 0, no peaks will be added from foot.
120  *
121  * If foot does not overlap it will do nothing.
122  */
123  void add(
124  FootprintMerge const & other,
125  FilterMap const & keys,
126  float minNewPeakDist=-1.,
127  float maxSamePeakDist=-1.
128  ) {
129  if (_addSpans(other.getMergedFootprint())) {
130  _footprints.insert(_footprints.end(), other._footprints.begin(), other._footprints.end());
131  // Set source flags to the OR of the flags of the two inputs
132  for (FilterMap::const_iterator i = keys.begin(); i != keys.end(); ++i) {
133  afw::table::Key<afw::table::Flag> const & flagKey = i->second.footprint;
134  _source->set(flagKey, _source->get(flagKey) || other._source->get(flagKey));
135  }
136  _addPeaks(other.getMergedFootprint()->getPeaks(), NULL, NULL, minNewPeakDist, maxSamePeakDist);
137  }
138  }
139 
140  // Get the bounding box of the merge
141  afw::geom::Box2I getBBox() const { return getMergedFootprint()->getBBox(); }
142 
143  PTR(Footprint) getMergedFootprint() const { return _source->getFootprint(); }
144 
146 
147 private:
148 
149  // Implementation helper for add() methods; returns true if the Footprint actually overlapped
150  // and was merged, and false otherwise.
151  bool _addSpans(PTR(Footprint) footprint) {
152  FootprintSet fpSet = mergeFootprintPair(*getMergedFootprint(), *footprint);
153  if (fpSet.getFootprints()->size() != 1u) return false;
154  getMergedFootprint()->_bbox.include(footprint->getBBox());
155  getMergedFootprint()->getSpans().swap(fpSet.getFootprints()->front()->getSpans());
156  return true;
157  }
158 
159  void _addPeaks(
160  PeakCatalog const & otherPeaks,
161  afw::table::SchemaMapper const * peakSchemaMapper,
162  KeyTuple const * keys,
163  float minNewPeakDist,
164  float maxSamePeakDist
165  ) {
166  if (minNewPeakDist < 0 && maxSamePeakDist < 0) return;
167 
168  PeakCatalog & currentPeaks = getMergedFootprint()->getPeaks();
169  PTR(PeakRecord) nearestPeak;
170  // Create new list of peaks
171  PeakCatalog newPeaks(currentPeaks.getTable());
172  float minNewPeakDist2 = minNewPeakDist*minNewPeakDist;
173  float maxSamePeakDist2 = maxSamePeakDist*maxSamePeakDist;
174  for (PeakCatalog::const_iterator otherIter = otherPeaks.begin();
175  otherIter != otherPeaks.end(); ++otherIter) {
176 
177  float minDist2 = std::numeric_limits<float>::infinity();
178 
179  for (PeakCatalog::const_iterator currentIter = currentPeaks.begin();
180  currentIter != currentPeaks.end(); ++currentIter) {
181  float dist2 = otherIter->getI().distanceSquared(currentIter->getI());
182 
183  if (dist2 < minDist2) {
184  minDist2 = dist2;
185  nearestPeak = currentIter;
186  }
187  }
188 
189  if (minDist2 < maxSamePeakDist2 && nearestPeak && keys && maxSamePeakDist > 0) {
190  nearestPeak->set(keys->peak, true);
191  } else if (minDist2 > minNewPeakDist2 && !(minNewPeakDist < 0)) {
192  if (peakSchemaMapper) {
193  PTR(PeakRecord) newPeak = newPeaks.addNew();
194  newPeak->assign(*otherIter, *peakSchemaMapper);
195  newPeak->set(keys->peak, true);
196  } else {
197  newPeaks.push_back(otherIter);
198  }
199  }
200 
201  }
202 
203  getMergedFootprint()->getPeaks().insert(
204  getMergedFootprint()->getPeaks().end(),
205  newPeaks.begin(), newPeaks.end(),
206  true // deep-copy
207  );
208  }
209 
210  std::vector<PTR(Footprint)> _footprints;
212 };
213 
214 
216  afw::table::Schema & sourceSchema,
217  std::vector<std::string> const & filterList,
218  afw::table::Schema const & initialPeakSchema
219 ) : _peakSchemaMapper(initialPeakSchema) {
220  _initialize(sourceSchema, filterList);
221 }
222 
224  afw::table::Schema & sourceSchema,
225  std::vector<std::string> const & filterList
226 ) : _peakSchemaMapper(PeakTable::makeMinimalSchema()) {
227  _initialize(sourceSchema, filterList);
228 }
229 
231  afw::table::Schema & sourceSchema,
232  std::vector<std::string> const & filterList
233 ) {
235  // Add Flags for the filters
236  for (
237  std::vector<std::string>::const_iterator iter=filterList.begin();
238  iter != filterList.end();
239  ++iter
240  ) {
241  KeyTuple & keys = _filterMap[*iter];
242  keys.footprint = sourceSchema.addField<afw::table::Flag>(
243  "merge_footprint_" + *iter,
244  "Detection footprint overlapped with a detection from filter " + *iter
245  );
246  keys.peak = _peakSchemaMapper.editOutputSchema().addField<afw::table::Flag>(
247  "merge_peak_" + *iter,
248  "Peak detected in filter " + *iter
249  );
250  }
252 }
253 
255  PTR(afw::table::SourceTable) sourceTable,
256  afw::table::SourceCatalog const &inputCat,
257  std::string const & filter,
258  float minNewPeakDist, bool doMerge, float maxSamePeakDist
259 ) {
260  FilterMap::const_iterator keyIter = _filterMap.find(filter);
261  if (keyIter == _filterMap.end()) {
262  throw LSST_EXCEPT(
263  pex::exceptions::LogicError,
264  (boost::format("Filter %s not in original list") % filter).str()
265  );
266  }
267 
268  // If list is empty don't check for any matches, just add all the objects
269  bool checkForMatches = (_mergeList.size() > 0);
270 
271  for (afw::table::SourceCatalog::const_iterator srcIter = inputCat.begin(); srcIter != inputCat.end();
272  ++srcIter) {
273 
274  // Only consider unblended objects
275  if (srcIter->getParent() != 0) continue;
276 
277  PTR(Footprint) foot = srcIter->getFootprint();
278 
279  // Empty pointer to account for the first match in the catalog. If there is more than one
280  // match, subsequent matches will be merged with this one
281  PTR(FootprintMerge) first = PTR(FootprintMerge)();
282 
283  if (checkForMatches) {
284  FootprintMergeVec::iterator iter = _mergeList.begin();
285  while (iter != _mergeList.end()) {
286  // Grow by one pixel to allow for touching
287  geom::Box2I box((**iter).getBBox());
288  box.grow(geom::Extent2I(1,1));
289  if (box.overlaps(foot->getBBox()) && (**iter).overlaps(*foot)) {
290  if (!first) {
291  first = *iter;
292  // Add Footprint to existing merge and set flag for this band
293  if (doMerge) {
294  first->add(foot, _peakSchemaMapper, keyIter->second, minNewPeakDist,
295  maxSamePeakDist);
296  }
297  } else {
298  // Add merged Footprint to first
299  if (doMerge) {
300  first->add(**iter, _filterMap, minNewPeakDist, maxSamePeakDist);
301  iter = _mergeList.erase(iter);
302  continue;
303  }
304  }
305  }
306  ++iter;
307  }
308  }
309 
310  if (!first) {
311  _mergeList.push_back(
312  boost::make_shared<FootprintMerge>(
313  foot, sourceTable, _peakTable, _peakSchemaMapper, keyIter->second
314  )
315  );
316  }
317  }
318 }
319 
321 {
322  // Now set the merged footprint as the footprint of the SourceRecord
323  for (FootprintMergeVec::iterator iter = _mergeList.begin(); iter != _mergeList.end(); ++iter) {
324  if (doNorm) (**iter).getMergedFootprint()->normalize();
325  outputCat.push_back((**iter).getSource());
326  }
327 }
328 
329 }}} // namespace lsst::afw::detection
int iter
Defines the fields and offsets for a table.
Definition: Schema.h:46
std::map< std::string, KeyTuple > FilterMap
Schema const getOutputSchema() const
Return the output schema (copy-on-write).
Definition: SchemaMapper.h:26
Schema const getInputSchema() const
Return the input schema (copy-on-write).
Definition: SchemaMapper.h:23
bool _addSpans(boost::shared_ptr< Footprint > footprint)
boost::shared_ptr< FootprintList > getFootprints()
Definition: FootprintSet.h:146
FootprintMergeList::FilterMap FilterMap
FootprintMerge(boost::shared_ptr< Footprint > footprint, boost::shared_ptr< afw::table::SourceTable > sourceTable, boost::shared_ptr< PeakTable > peakTable, afw::table::SchemaMapper const &peakSchemaMapper, KeyTuple const &keys)
A mapping between the keys of two Schemas, used to copy data between them.
Definition: SchemaMapper.h:19
void include(Point2I const &point)
Expand this to ensure that this-&gt;contains(point).
boost::shared_ptr< Footprint > getMergedFootprint() const
Table class for Peaks in Footprints.
Definition: Peak.h:84
static boost::shared_ptr< PeakTable > make(afw::table::Schema const &schema, bool forceNew=false)
Obtain a table that can be used to create records with given schema.
Definition: Peak.cc:154
#define PTR(...)
Definition: base.h:41
void _initialize(afw::table::Schema &sourceSchema, std::vector< std::string > const &filterList)
Use (pixels &amp; (given mask))
Definition: Threshold.h:49
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
An integer coordinate rectangle.
Definition: Box.h:53
Represent a collections of footprints associated with image data.
bool overlaps(Footprint const &rhs) const
void addCatalog(boost::shared_ptr< afw::table::SourceTable > sourceTable, afw::table::SourceCatalog const &inputCat, std::string const &filter, float minNewPeakDist=-1., bool doMerge=true, float maxSamePeakDist=-1.)
Add objects from a SourceCatalog in the specified filter.
void push_back(Record const &r)
Add a copy of the given record to the end of the catalog.
Definition: Catalog.h:458
FootprintMergeList::KeyTuple KeyTuple
afw::table::CatalogT< PeakRecord > PeakCatalog
Definition: Peak.h:225
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 _addPeaks(PeakCatalog const &otherPeaks, afw::table::SchemaMapper const *peakSchemaMapper, KeyTuple const *keys, float minNewPeakDist, float maxSamePeakDist)
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:93
boost::shared_ptr< PeakRecord > copyRecord(afw::table::BaseRecord const &other)
Definition: Peak.h:158
Iterator class for CatalogT.
Definition: Catalog.h:34
void add(boost::shared_ptr< Footprint > footprint, afw::table::SchemaMapper const &peakSchemaMapper, KeyTuple const &keys, float minNewPeakDist=-1., float maxSamePeakDist=-1.)
boost::shared_ptr< afw::table::SourceRecord > getSource() const
Schema & editOutputSchema()
Return a reference to the output schema that allows it to be modified in place.
Definition: SchemaMapper.h:29
afw::geom::Box2I getBBox() const
A set of pixels in an Image.
Definition: Footprint.h:62
void getFinalSources(afw::table::SourceCatalog &outputCat, bool doNorm=true)
Get SourceCatalog with entries that contain the final Footprint and SourceRecord for each entry...
Table class that contains measurements made on a single exposure.
Definition: Source.h:203
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
A set of Footprints, associated with a MaskedImage.
Definition: FootprintSet.h:53
void grow(int buffer)
Increase the size of the box by the given buffer amount in all directions.
Definition: Box.h:193
void addMinimalSchema(Schema const &minimal, bool doMap=true)
Add the given minimal schema to the output schema.
boost::shared_ptr< PeakTable > _peakTable
std::vector< boost::shared_ptr< Footprint > > _footprints
boost::shared_ptr< Table > getTable() const
Return the table associated with the catalog.
Definition: Catalog.h:111
lsst::afw::detection::Footprint Footprint
Definition: Source.h:61
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.
afw::table::SchemaMapper _peakSchemaMapper
FootprintMergeList(afw::table::Schema &sourceSchema, std::vector< std::string > const &filterList, afw::table::Schema const &initialPeakSchema)
boost::shared_ptr< afw::table::SourceRecord > _source
Record class that contains measurements made on a single exposure.
Definition: Source.h:81
void add(FootprintMerge const &other, FilterMap const &keys, float minNewPeakDist=-1., float maxSamePeakDist=-1.)
Record class that represents a peak in a Footprint.
Definition: Peak.h:40
geom::Box2I getBBox() const
Return the Footprint&#39;s bounding box.
Definition: Footprint.h:206