LSST Applications g06d8191974+de063e15a7,g180d380827+d0b6459378,g2079a07aa2+86d27d4dc4,g2305ad1205+f1ae3263cc,g29320951ab+5752d78b6e,g2bbee38e9b+85cf0a37e7,g337abbeb29+85cf0a37e7,g33d1c0ed96+85cf0a37e7,g3a166c0a6a+85cf0a37e7,g3ddfee87b4+b5254b9343,g48712c4677+9ea88d309d,g487adcacf7+05f7dba17f,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+48904e3942,g64a986408d+de063e15a7,g858d7b2824+de063e15a7,g864b0138d7+33ab2bc355,g8a8a8dda67+585e252eca,g99cad8db69+4508353287,g9c22b2923f+53520f316c,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+ccb7f83a87,gc120e1dc64+6caf640b9b,gc28159a63d+85cf0a37e7,gc3e9b769f7+548c5e05a3,gcf0d15dbbd+b5254b9343,gdaeeff99f8+f9a426f77a,ge6526c86ff+515b6c9330,ge79ae78c31+85cf0a37e7,gee10cc3b42+585e252eca,gff1a9f87cc+de063e15a7,w.2024.17
LSST Data Management Base Package
Loading...
Searching...
No Matches
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 <cstdint>
23
26
27namespace lsst {
28namespace afw {
29namespace detection {
30
32public:
33 using KeyTuple = FootprintMergeList::KeyTuple;
35
39 afw::table::SchemaMapper const &peakSchemaMapper, KeyTuple const &keys)
40 : _footprints(1, footprint), _source(sourceTable->makeRecord()) {
41 std::shared_ptr<Footprint> newFootprint = std::make_shared<Footprint>(*footprint);
42
43 _source->set(keys.footprint, true);
44 // Replace all the Peaks in the merged Footprint with new ones that include the origin flags
45 newFootprint->getPeaks() = PeakCatalog(peakTable);
46 for (PeakCatalog::iterator iter = footprint->getPeaks().begin(); iter != footprint->getPeaks().end();
47 ++iter) {
48 std::shared_ptr<PeakRecord> newPeak = peakTable->copyRecord(*iter, peakSchemaMapper);
49 newPeak->set(keys.peak, true);
50 newPeak->setId((*newFootprint->getPeaks().getTable()->getIdFactory())());
51 newFootprint->getPeaks().push_back(newPeak);
52 }
53 _source->setFootprint(newFootprint);
54 }
55
56 ~FootprintMerge() = default;
57 FootprintMerge(FootprintMerge const &) = default;
61
62 /*
63 * Does this Footprint overlap the merged Footprint.
64 *
65 * The current implementation just builds an image from the two Footprints and
66 * detects the number of peaks. This is not very efficient and will be changed
67 * within the Footprint class in the future.
68 */
69 bool overlaps(Footprint const &rhs) const {
70 return getMergedFootprint()->getSpans()->overlaps(*(rhs.getSpans()));
71 }
72
73 /*
74 * Add this Footprint to the merge.
75 *
76 * If minNewPeakDist >= 0, it will add all peaks from the footprint to the merged Footprint
77 * that are greater than minNewPeakDist away from the closest peak in the existing list.
78 * If minNewPeakDist < 0, no peaks will be added from the footprint.
79 *
80 * If maxSamePeakDist >= 0, it will find the closest peak in the existing list for every peak
81 * in the footprint. If the closest peak is less than maxSamePeakDist, the peak will not
82 * be added and the closest peak will be flagged as detected by the filter defined in keys.
83 *
84 * If the footprint does not overlap it will do nothing.
85 */
86 void add(std::shared_ptr<Footprint> footprint, afw::table::SchemaMapper const &peakSchemaMapper,
87 KeyTuple const &keys, float minNewPeakDist = -1., float maxSamePeakDist = -1.) {
88 if (addSpans(footprint)) {
89 _footprints.push_back(footprint);
90 _source->set(keys.footprint, true);
91 _addPeaks(footprint->getPeaks(), &peakSchemaMapper, &keys, minNewPeakDist, maxSamePeakDist, nullptr);
92 }
93 }
94
95 /*
96 * Merge an already-merged clump of Footprints into this
97 *
98 * If minNewPeakDist >= 0, it will add all peaks from the footprint to the merged Footprint
99 * that are greater than minNewPeakDist away from the closest peak in the existing list.
100 * If minNewPeakDist < 0, no peaks will be added from the footprint.
101 *
102 * If maxSamePeakDist >= 0, it will find the closest peak in the existing list for every peak
103 * in the footprint. If the closest peak is less than maxSamePeakDist, the peak will not
104 * be added to the list and the flags from the closest peak will be set to the OR of the two.
105 *
106 * If the FootprintMerge does not overlap it will do nothing.
107 */
108 void add(FootprintMerge const &other, FilterMap const &keys, float minNewPeakDist = -1.,
109 float maxSamePeakDist = -1.) {
110 if (addSpans(other.getMergedFootprint())) {
111 _footprints.insert(_footprints.end(), other._footprints.begin(), other._footprints.end());
112 // Set source flags to the OR of the flags of the two inputs
113 for (auto const &key : keys) {
114 afw::table::Key<afw::table::Flag> const &flagKey = key.second.footprint;
115 _source->set(flagKey, _source->get(flagKey) || other._source->get(flagKey));
116 }
117 _addPeaks(other.getMergedFootprint()->getPeaks(), nullptr, nullptr, minNewPeakDist, maxSamePeakDist,
118 &keys);
119 }
120 }
121
122 // Get the bounding box of the merge
123 lsst::geom::Box2I getBBox() const { return getMergedFootprint()->getBBox(); }
124
125 std::shared_ptr<Footprint> getMergedFootprint() const { return _source->getFootprint(); }
126
128
129 // Implementation helper for add() methods; returns true if the Footprint actually overlapped
130 // and was merged, and false otherwise.
132 if (!getMergedFootprint()->getSpans()->overlaps(*(footprint->getSpans()))) return false;
133 getMergedFootprint()->setSpans(getMergedFootprint()->getSpans()->union_(*(footprint->getSpans())));
134 return true;
135 }
136
137private:
138 /*
139 * Add new peaks to the list of peaks of the merged footprint.
140 * This function handles two different cases:
141 * - The peaks come from a single footprint. In this case, the peakSchemaMapper
142 * and keys should be defined so that it can create a new peak, copy the appropriate
143 * data, and set the peak flag defined in keys.
144 * - The peaks come from another FootprintMerge. In this case, filterMap should
145 * be defined so that the information from the other peaks can be propagated.
146 */
147 void _addPeaks(PeakCatalog const &otherPeaks, afw::table::SchemaMapper const *peakSchemaMapper,
148 KeyTuple const *keys, float minNewPeakDist, float maxSamePeakDist,
149 FilterMap const *filterMap) {
150 if (minNewPeakDist < 0 && maxSamePeakDist < 0) return;
151
152 assert(peakSchemaMapper || filterMap);
153
154 PeakCatalog &currentPeaks = getMergedFootprint()->getPeaks();
155 std::shared_ptr<PeakRecord> nearestPeak;
156 // Create new list of peaks
157 PeakCatalog newPeaks(currentPeaks.getTable());
158 float minNewPeakDist2 = minNewPeakDist * minNewPeakDist;
159 float maxSamePeakDist2 = maxSamePeakDist * maxSamePeakDist;
160 for (PeakCatalog::const_iterator otherIter = otherPeaks.begin(); otherIter != otherPeaks.end();
161 ++otherIter) {
162 float minDist2 = std::numeric_limits<float>::infinity();
163
164 for (PeakCatalog::const_iterator currentIter = currentPeaks.begin();
165 currentIter != currentPeaks.end(); ++currentIter) {
166 float dist2 = otherIter->getI().distanceSquared(currentIter->getI());
167
168 if (dist2 < minDist2) {
169 minDist2 = dist2;
170 nearestPeak = currentIter;
171 }
172 }
173
174 if (minDist2 < maxSamePeakDist2 && nearestPeak && maxSamePeakDist > 0) {
175 if (peakSchemaMapper) {
176 nearestPeak->set(keys->peak, true);
177 } else {
178 for (auto const &i : *filterMap) {
179 afw::table::Key<afw::table::Flag> const &flagKey = i.second.peak;
180 nearestPeak->set(flagKey, nearestPeak->get(flagKey) || otherIter->get(flagKey));
181 }
182 }
183 } else if (minDist2 > minNewPeakDist2 && !(minNewPeakDist < 0)) {
184 if (peakSchemaMapper) {
185 std::shared_ptr<PeakRecord> newPeak = newPeaks.addNew();
186 newPeak->assign(*otherIter, *peakSchemaMapper);
187 newPeak->set(keys->peak, true);
188 newPeak->setId((*currentPeaks.getTable()->getIdFactory())());
189 } else {
190 newPeaks.push_back(otherIter);
191 }
192 }
193 }
194
195 getMergedFootprint()->getPeaks().insert(getMergedFootprint()->getPeaks().end(), newPeaks.begin(),
196 newPeaks.end(),
197 true // deep-copy
198 );
199 }
200
203};
204
206 std::vector<std::string> const &filterList,
207 afw::table::Schema const &initialPeakSchema)
208 : _peakSchemaMapper(initialPeakSchema) {
209 _initialize(sourceSchema, filterList);
210}
211
213 std::vector<std::string> const &filterList)
214 : _peakSchemaMapper(PeakTable::makeMinimalSchema()) {
215 _initialize(sourceSchema, filterList);
216}
217
223
224void FootprintMergeList::_initialize(afw::table::Schema &sourceSchema,
225 std::vector<std::string> const &filterList) {
226 _peakSchemaMapper.addMinimalSchema(_peakSchemaMapper.getInputSchema(), true);
227 // Add Flags for the filters
228 for (auto const &iter : filterList) {
229 KeyTuple &keys = _filterMap[iter];
230 keys.footprint = sourceSchema.addField<afw::table::Flag>(
231 "merge_footprint_" + iter,
232 "Detection footprint overlapped with a detection from filter " + iter);
233 keys.peak = _peakSchemaMapper.editOutputSchema().addField<afw::table::Flag>(
234 "merge_peak_" + iter, "Peak detected in filter " + iter);
235 }
236 _peakTable = PeakTable::make(_peakSchemaMapper.getOutputSchema());
237}
238
240 afw::table::SourceCatalog const &inputCat, std::string const &filter,
241 float minNewPeakDist, bool doMerge, float maxSamePeakDist) {
242 FilterMap::const_iterator keyIter = _filterMap.find(filter);
243 if (keyIter == _filterMap.end()) {
245 (boost::format("Filter %s not in original list") % filter).str());
246 }
247
248 // If list is empty or merging not requested, don't check for any matches, just add all the objects
249 bool checkForMatches = !_mergeList.empty() && doMerge;
250
251 for (afw::table::SourceCatalog::const_iterator srcIter = inputCat.begin(); srcIter != inputCat.end();
252 ++srcIter) {
253 // Only consider unblended objects
254 if (srcIter->getParent() != 0) continue;
255
256 std::shared_ptr<Footprint> foot = srcIter->getFootprint();
257
258 // Empty pointer to account for the first match in the catalog. If there is more than one
259 // match, subsequent matches will be merged with this one
261
262 if (checkForMatches) {
263 FootprintMergeVec::iterator iter = _mergeList.begin();
264 while (iter != _mergeList.end()) {
265 // Grow by one pixel to allow for touching
266 lsst::geom::Box2I box((**iter).getBBox());
267 box.grow(lsst::geom::Extent2I(1, 1));
268 if (box.overlaps(foot->getBBox()) && (**iter).overlaps(*foot)) {
269 if (!first) {
270 first = *iter;
271 // Spatially extend existing FootprintMerge in order to connect subsequent,
272 // now-overlapping FootprintMerges. If a subsequent FootprintMerge overlaps with
273 // the new footprint, it's now guaranteed to overlap with this first FootprintMerge.
274 // Hold off adding foot's lower-priority footprints and peaks until the
275 // higher-priority existing peaks are merged into this first FootprintMerge.
276 first->addSpans(foot);
277 } else {
278 // Add existing merged Footprint to first
279 first->add(**iter, _filterMap, minNewPeakDist, maxSamePeakDist);
280 iter = _mergeList.erase(iter);
281 continue;
282 }
283 }
284 ++iter;
285 } // while mergeList
286 } // if checkForMatches
287
288 if (first) {
289 // Now merge footprint including peaks into the newly-connected, higher-priority FootprintMerge
290 first->add(foot, _peakSchemaMapper, keyIter->second, minNewPeakDist, maxSamePeakDist);
291 } else {
292 // Footprint did not overlap with any existing FootprintMerges. Add to MergeList
293 _mergeList.push_back(std::make_shared<FootprintMerge>(foot, sourceTable, _peakTable,
294 _peakSchemaMapper, keyIter->second));
295 }
296 }
297}
298
300 // Now set the merged footprint as the footprint of the SourceRecord
301 for (auto const &iter : _mergeList) {
302 outputCat.push_back((*iter).getSource());
303 }
304}
305} // namespace detection
306} // namespace afw
307} // namespace lsst
int end
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
T begin(T... args)
Class to describe the properties of a detected object from an image.
Definition Footprint.h:63
std::shared_ptr< geom::SpanSet > getSpans() const
Return a shared pointer to the SpanSet.
Definition Footprint.h:115
bool overlaps(Footprint const &rhs) const
bool addSpans(std::shared_ptr< Footprint > footprint)
std::shared_ptr< afw::table::SourceRecord > getSource() const
FootprintMergeList::FilterMap FilterMap
FootprintMerge & operator=(FootprintMerge &&)=default
FootprintMerge(FootprintMerge &&)=default
std::shared_ptr< Footprint > getMergedFootprint() const
FootprintMergeList::KeyTuple KeyTuple
FootprintMerge(std::shared_ptr< Footprint > footprint, std::shared_ptr< afw::table::SourceTable > sourceTable, std::shared_ptr< PeakTable > peakTable, afw::table::SchemaMapper const &peakSchemaMapper, KeyTuple const &keys)
FootprintMerge & operator=(FootprintMerge const &)=default
void add(std::shared_ptr< Footprint > footprint, afw::table::SchemaMapper const &peakSchemaMapper, KeyTuple const &keys, float minNewPeakDist=-1., float maxSamePeakDist=-1.)
lsst::geom::Box2I getBBox() const
FootprintMerge(FootprintMerge const &)=default
void add(FootprintMerge const &other, FilterMap const &keys, float minNewPeakDist=-1., float maxSamePeakDist=-1.)
FootprintMergeList(afw::table::Schema &sourceSchema, std::vector< std::string > const &filterList, afw::table::Schema const &initialPeakSchema)
Initialize the merge with a custom initial peak schema.
void getFinalSources(afw::table::SourceCatalog &outputCat)
Get SourceCatalog with entries that contain the final Footprint and SourceRecord for each entry.
FootprintMergeList & operator=(FootprintMergeList const &)
void addCatalog(std::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.
Table class for Peaks in Footprints.
Definition Peak.h:102
static std::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:96
Tag types used to declare specialized field types.
Definition misc.h:31
iterator begin()
Iterator access.
Definition Catalog.h:400
std::shared_ptr< Table > getTable() const
Return the table associated with the catalog.
Definition Catalog.h:114
Defines the fields and offsets for a table.
Definition Schema.h:51
Key< T > addField(Field< T > const &field, bool doReplace=false)
Add a new field to the Schema, and return the associated Key.
Definition Schema.cc:479
A mapping between the keys of two Schemas, used to copy data between them.
Schema const getOutputSchema() const
Return the output schema (copy-on-write).
Schema & editOutputSchema()
Return a reference to the output schema that allows it to be modified in place.
void addMinimalSchema(Schema const &minimal, bool doMap=true)
Add the given minimal schema to the output schema.
Schema const getInputSchema() const
Return the input schema (copy-on-write).
typename Base::const_iterator const_iterator
An integer coordinate rectangle.
Definition Box.h:55
bool overlaps(Box2I const &other) const noexcept
Return true if any points in other are also in this.
Definition Box.cc:122
void grow(int buffer)
Increase the size of the box by the given buffer amount in all directions.
Definition Box.h:249
Reports errors in the logical structure of the program.
Definition Runtime.h:46
T empty(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
T get(T... args)
T infinity(T... args)
afw::table::CatalogT< PeakRecord > PeakCatalog
Definition Peak.h:244
T push_back(T... args)