LSST Applications g063fba187b+cac8b7c890,g0f08755f38+6aee506743,g1653933729+a8ce1bb630,g168dd56ebc+a8ce1bb630,g1a2382251a+b4475c5878,g1dcb35cd9c+8f9bc1652e,g20f6ffc8e0+6aee506743,g217e2c1bcf+73dee94bd0,g28da252d5a+1f19c529b9,g2bbee38e9b+3f2625acfc,g2bc492864f+3f2625acfc,g3156d2b45e+6e55a43351,g32e5bea42b+1bb94961c2,g347aa1857d+3f2625acfc,g35bb328faa+a8ce1bb630,g3a166c0a6a+3f2625acfc,g3e281a1b8c+c5dd892a6c,g3e8969e208+a8ce1bb630,g414038480c+5927e1bc1e,g41af890bb2+8a9e676b2a,g7af13505b9+809c143d88,g80478fca09+6ef8b1810f,g82479be7b0+f568feb641,g858d7b2824+6aee506743,g89c8672015+f4add4ffd5,g9125e01d80+a8ce1bb630,ga5288a1d22+2903d499ea,gb58c049af0+d64f4d3760,gc28159a63d+3f2625acfc,gcab2d0539d+b12535109e,gcf0d15dbbd+46a3f46ba9,gda6a2b7d83+46a3f46ba9,gdaeeff99f8+1711a396fd,ge79ae78c31+3f2625acfc,gef2f8181fd+0a71e47438,gf0baf85859+c1f95f4921,gfa517265be+6aee506743,gfa999e8aa5+17cd334064,w.2024.51
LSST Data Management Base Package
Loading...
Searching...
No Matches
SpanSet.cc
Go to the documentation of this file.
1
2/*
3 * LSST Data Management System
4 * Copyright 2008-2016 AURA/LSST.
5 *
6 * This product includes software developed by the
7 * LSST Project (http://www.lsst.org/).
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the LSST License Statement and
20 * the GNU General Public License along with this program. If not,
21 * see <https://www.lsstcorp.org/LegalNotices/>.
22 */
23
24#include <algorithm>
25#include <iterator>
33
34namespace lsst {
35namespace afw {
36
39
40namespace geom {
41namespace {
42
43/* These classes are used in the erode operator to quickly calculate the
44 * contents of the shrunken SpanSet
45 */
46
47struct PrimaryRun {
48 int m, y, xmin, xmax;
49};
50
51bool comparePrimaryRun(PrimaryRun const& first, PrimaryRun const& second) {
52 if (first.y != second.y) {
53 return first.y < second.y;
54 } else if (first.m != second.m) {
55 return first.m < second.m;
56 } else {
57 return first.xmin < second.xmin;
58 }
59}
60
61class ComparePrimaryRunY {
62public:
63 bool operator()(PrimaryRun const& pr, int yval) { return pr.y < yval; }
64 bool operator()(int yval, PrimaryRun const& pr) { return yval < pr.y; }
65};
66
67class ComparePrimaryRunM {
68public:
69 bool operator()(PrimaryRun const& pr, int mval) { return pr.m < mval; }
70 bool operator()(int mval, PrimaryRun const& pr) { return mval < pr.m; }
71};
72
73/* Determine if two spans overlap
74 *
75 * a First Span in comparison
76 * b Second Span in comparison
77 * compareY a boolean to control if the comparison takes into account the y position of the spans
78 */
79bool spansOverlap(Span const& a, Span const& b, bool compareY = true) {
80 bool yTruth = true;
81 if (compareY) {
82 yTruth = a.getY() == b.getY();
83 }
84 return (yTruth && ((a.getMaxX() >= b.getMinX() && a.getMinX() <= b.getMinX()) ||
85 (b.getMaxX() >= a.getMinX() && b.getMinX() <= a.getMinX())))
86 ? true
87 : false;
88}
89
90/* Determine if two spans are contiguous, that is they can overlap or the end of one span is
91 * one pixel before the beginning of the next
92 *
93 * a First Span in comparison
94 * b Second Span in comparison
95 * compareY a boolean to control if the comparison takes into account the y position of the spans
96 */
97bool spansContiguous(Span const& a, Span const& b, bool compareY = true) {
98 bool yTruth(true);
99 if (compareY) {
100 yTruth = a.getY() == b.getY();
101 }
102 return (yTruth && ((a.getMaxX() + 1 >= b.getMinX() && a.getMinX() <= b.getMinX()) ||
103 (b.getMaxX() + 1 >= a.getMinX() && b.getMinX() <= a.getMinX())))
104 ? true
105 : false;
106}
107
108/* Determine the intersection with a mask or its logical inverse
109 *
110 * spanSet - SpanSet object with which to intersect the mask
111 * mask - Mask object to be intersectedNot
112 * bitmask - bitpattern to used when deciding pixel membership in the mask
113 *
114 * Templates:
115 * T - pixel type of the mask
116 * invert - boolean indicating if the intersection is with the mask or the inverse of the mask
117 templated parameter to ensure the check is compiled away if it can be
118*/
119template <typename T, bool invert>
120std::shared_ptr<SpanSet> maskIntersect(SpanSet const& spanSet, image::Mask<T> const& mask, T bitmask) {
121 // This vector will store our output spans
122 std::vector<Span> newVec;
123 auto maskBBox = mask.getBBox();
124 for (auto const& spn : spanSet) {
125 // Variable to mark if a new span has been started
126 // Reset the started flag for each different span
127 bool started = false;
128 // Limit the y iteration to be within the mask's bounding box
129 int y = spn.getY();
130 if (y < maskBBox.getMinY() || y > maskBBox.getMaxY()) {
131 continue;
132 }
133 // Reset the min and max variables
134 int minX = 0;
135 int maxX = 0;
136 // Limit the scope of iteration to be within the mask's bounds
137 int startX = std::max(spn.getMinX(), maskBBox.getMinX());
138 int endX = std::min(spn.getMaxX(), maskBBox.getMaxX());
139 for (int x = startX; x <= endX; ++x) {
140 // Find if the pixel matches the given bit pattern
141 bool pixelCompare = mask.get(lsst::geom::Point2I(x, y), image::ImageOrigin::PARENT) & bitmask;
142 // if the templated boolean indicates the compliment of the mask is desired, invert the
143 // pixel comparison
144 if (invert) {
145 pixelCompare = !pixelCompare;
146 }
147 // If the pixel is to be included in the operation, start or append recording values
148 // needed to later construct a Span
149 if (pixelCompare) {
150 if (!started) {
151 started = true;
152 minX = x;
153 maxX = x;
154 } else {
155 maxX = x;
156 }
157 if (x == endX) {
158 // We have reached the end of a row and the pixel evaluates true, should add the
159 // Span
160 newVec.emplace_back(y, minX, maxX);
161 }
162 } else if (started) {
163 // A span was started but the current pixel is not to be included in the new SpanSet,
164 // the current SpanSet should be close and added to the vector
165 newVec.emplace_back(y, minX, maxX);
166 started = false;
167 }
168 }
169 }
170 return std::make_shared<SpanSet>(std::move(newVec));
171}
172
173} // namespace
174
175// Default constructor, creates a null SpanSet which may be useful for
176// comparisons
177SpanSet::SpanSet() : _spanVector(), _bbox(), _area(0) {}
178
179// Construct a SpanSet from an lsst::geom::Box2I object
180SpanSet::SpanSet(lsst::geom::Box2I const& box) : _bbox(box), _area(box.getArea()) {
181 int beginY = box.getMinY();
182
183 int beginX = box.getMinX();
184 int maxX = box.getMaxX();
185
186 for (int i = beginY; i < _bbox.getEndY(); ++i) {
187 _spanVector.emplace_back(i, beginX, maxX);
188 }
189}
190
191// Construct a SpanSet from a std vector by copying
192SpanSet::SpanSet(std::vector<Span> const& vec, bool normalize) : _spanVector(vec) {
193 // If the incoming vector is zero, should create an empty spanSet
194 if (_spanVector.empty()) {
195 _bbox = lsst::geom::Box2I();
196 _area = 0;
197 } else {
198 if (normalize) {
199 _runNormalize();
200 }
201 _initialize();
202 }
203}
204
205// Construct a SpanSet from a std vector by moving
206SpanSet::SpanSet(std::vector<Span>&& vec, bool normalize) : _spanVector(std::move(vec)) {
207 // If the incoming vector is zero, should create an empty SpanSet
208 if (_spanVector.size() == 0) {
209 _bbox = lsst::geom::Box2I();
210 _area = 0;
211 } else {
212 if (normalize) {
213 _runNormalize();
214 }
215 _initialize();
216 }
217}
218
219void SpanSet::_runNormalize() {
220 // This bit of code is not safe if the _spanVector is empty. However, this function will only be executed
221 // with a non-empty _spanVector as it is called internally by the class constructors, and cannot be
222 // executed from outside the class
223
224 // Ensure the span set is sorted according to Span < operator
225 std::sort(_spanVector.begin(), _spanVector.end());
226
227 // Create a new vector to hold the possibly combined Spans
228 std::vector<Span> newSpans;
229 // Reserve the size of the original as it is the maximum possible size
230 newSpans.reserve(_spanVector.size());
231 // push back the first element, as it is certain to be included
232 newSpans.push_back(*_spanVector.begin());
233
234 // With the sorted span array, spans that are contiguous with the end of the last span in the new vector
235 // should be combined with the last span in the new vector. Only when there is no longer continuity
236 // between spans should a new span be added.
237 // Start iteration from "1" as the 0th element is already in the newSpans vector
238 for (auto iter = ++(_spanVector.begin()); iter != _spanVector.end(); ++iter) {
239 auto& newSpansEnd = newSpans.back();
240 if (spansContiguous(newSpansEnd, *iter)) {
241 newSpansEnd = Span(newSpansEnd.getY(), std::min(newSpansEnd.getMinX(), iter->getMinX()),
242 std::max(newSpansEnd.getMaxX(), iter->getMaxX()));
243 } else {
244 newSpans.emplace_back(*iter);
245 }
246 }
247
248 // Set the new normalized vector of spans to the internal span vector container
249 _spanVector = std::move(newSpans);
250}
251
252void SpanSet::_initialize() {
253 /* This function exists to handle common functionality for most of the constructors. It calculates the
254 * bounding box for the SpanSet, and the area covered by the SpanSet
255 */
256
257 /* Because the array is sorted, the minimum and maximum values for Y will
258 be in the first and last elements, only need to find the min and max X
259 values */
260
261 // This bit of code will only be executed with a non-empty _spanVector internally
262 // and is not accessable from outside the class
263
264 int minX = _spanVector[0].getMinX();
265 int maxX = _spanVector[0].getMaxX();
266 _area = 0;
267
268 for (const auto& span : _spanVector) {
269 if (span.getMinX() < minX) {
270 minX = span.getMinX();
271 }
272 if (span.getMaxX() > maxX) {
273 maxX = span.getMaxX();
274 }
275 // Plus one, because end point is inclusive
276 _area += span.getMaxX() - span.getMinX() + 1;
277 }
278 _bbox = lsst::geom::Box2I(lsst::geom::Point2I(minX, _spanVector.front().getY()),
279 lsst::geom::Point2I(maxX, _spanVector.back().getY()));
280}
281
282// Getter for the area property
283std::size_t SpanSet::getArea() const { return _area; }
284
285// Getter for the bounding box of the SpanSet
286lsst::geom::Box2I SpanSet::getBBox() const { return _bbox; }
287
288/* Here is a description of how the _makeLabels and _label function works. In the
289 _makeLabels function, a vector is created with the same number of elements as
290 spans in the SpanSet. This vector will contain the label for each Span in the
291 SpanSet. These labels correspond to which connected region the span falls in.
292 If the whole SpanSet is connected than there will only be one region and every
293 Span will be labeled with a 1. If there are two regions (i.e. there are points
294 in between not contained in the SpanSet) then Some of the Spans will be labeled
295 1, and the rest labeled 2. _makeLabels also sorts each of the Spans in the
296 current span into an unordered map that is indexed by the y position of each
297 span. This allows efficient lookups of corresponding rows.
298
299 The function loops over all the Spans in the Spanset. If the Span has not been
300 labeled, the _label function is called, with the current span, the vector
301 containing the labels, the label for the current region under consideration,
302 and the indexed map of Spans.
303
304 The _label function will then loop over all Spans in the SpanSet looking for
305 rows adjacent to the currently being labeled Span which overlap in the x
306 dimension and have not yet been labeled, and mark them with the current label,
307 and recursively call the _label function with the adjacent row as the new Span
308 under consideration. This results in all Spans which overlap each other in x
309 being labeled with the current label.
310
311 Once _label reaches the end of Spans which meet it's criteria, control falls
312 back to the _makeLabels function and the current label is incremented. The
313 function then moves onto the next Span in the SpanSet which has not been
314 labeled. If all spans were labeled in the recursive call the loop falls to the
315 end, and the label vector and one past the last label number (number of labels +1)
316 are returned to the caller. If instead _makeLabels finds a Span that has not
317 been labeled, the next label is assigned and the process is repeated until
318 all Spans have been labeled.
319 */
320
321void SpanSet::_label(
322 Span const& spn, std::vector<std::size_t>& labelVector, std::size_t currentLabel,
324 auto currentIndex = spn.getY();
325 if (currentIndex > 0) {
326 // loop over the prevous row
327 for (auto const& tup : sortMap[currentIndex - 1]) {
328 if (!labelVector[tup.first] && spansOverlap(spn, *(tup.second), false)) {
329 labelVector[tup.first] = currentLabel;
330 _label(*(tup.second), labelVector, currentLabel, sortMap);
331 }
332 }
333 }
334 if (currentIndex <= _spanVector.back().getY() - 1) {
335 // loop over the next row
336 for (auto& tup : sortMap[currentIndex + 1]) {
337 if (!labelVector[tup.first] && spansOverlap(spn, *(tup.second), false)) {
338 labelVector[tup.first] = currentLabel;
339 _label(*(tup.second), labelVector, currentLabel, sortMap);
340 }
341 }
342 }
343}
344
345std::pair<std::vector<std::size_t>, std::size_t> SpanSet::_makeLabels() const {
346 std::vector<std::size_t> labelVector(_spanVector.size(), 0);
347 std::size_t currentLabel = 1;
348 std::size_t index = 0;
349 // Create a sorted array of arrays
351 std::size_t tempIndex = 0;
352 for (auto const& spn : _spanVector) {
353 sortMap[spn.getY()].push_back(std::make_pair(tempIndex, &spn));
354 tempIndex++;
355 }
356 for (auto const& currentSpan : _spanVector) {
357 if (!labelVector[index]) {
358 labelVector[index] = currentLabel;
359 _label(currentSpan, labelVector, currentLabel, sortMap);
360 /* At this point we have recursed enough to reach all of the connected
361 * region, and should increment the label such that any spans not
362 * labeled in the first loop will get a new value. If all of the spans
363 * were connected in the first pass, the rest of this loop will just
364 * fall though.
365 */
366 ++currentLabel;
367 }
368 ++index;
369 }
370 return std::pair<std::vector<std::size_t>, std::size_t>(labelVector, currentLabel);
371}
372
374 auto labeledPair = _makeLabels();
375 // Here we want to check if there is only one label. Since _makeLabels always increments
376 // at the end of each loop, we need to compare against the number 2. I.e. if everything
377 // gets labeled 1, the label counter will increment to 2, and the if statement will always
378 // be false and _makeLabels will fall through to the end and return to here.
379 return labeledPair.second <= 2;
380}
381
383 auto labeledPair = _makeLabels();
384 auto labels = labeledPair.first;
385 auto numberOfLabels = labeledPair.second;
387 // As the number of labels is known, the number of SpanSets to be created is also known
388 // make a vector of vectors to hold the Spans which will correspond to each SpanSet
389 std::vector<std::vector<Span>> subSpanLists(numberOfLabels - 1);
390
391 // if numberOfLabels is 1, that means a null SpanSet is being operated on,
392 // and we should return like
393 if (numberOfLabels == 1) {
394 subRegions.push_back(std::make_shared<SpanSet>());
395 return subRegions;
396 }
397 subRegions.reserve(numberOfLabels - 1);
398
399 // loop over the current SpanSet's spans sorting each of the spans according to the label
400 // that was assigned
401 for (std::size_t i = 0; i < _spanVector.size(); ++i) {
402 subSpanLists[labels[i] - 1].push_back(_spanVector[i]);
403 }
404 // Transform each of the vectors of Spans into a SpanSet
405 for (std::size_t i = 0; i < numberOfLabels - 1; ++i) {
406 subRegions.push_back(std::make_shared<SpanSet>(subSpanLists[i]));
407 }
408 return subRegions;
409}
410
413 setMask(tempMask, static_cast<image::MaskPixel>(1));
414 auto erodedSpanSet = eroded(1, Stencil::CIRCLE);
415 erodedSpanSet->clearMask(tempMask, static_cast<image::MaskPixel>(1));
416 return SpanSet::fromMask(tempMask);
417}
418
420 // Function to create a new SpanSet which is a copy of this, shifted by x and y
421 return makeShift(x, y);
422}
423
425 // Function to create a new SpanSet which is a copy of this, shifted by an extent object
426 return makeShift(offset.getX(), offset.getY());
427}
428
429std::shared_ptr<SpanSet> SpanSet::makeShift(int x, int y) const {
430 // Implementation method common to all overloads of the shiftedBy method
431 std::vector<Span> tempVec;
432 tempVec.reserve(_spanVector.size());
433 for (auto const& spn : _spanVector) {
434 tempVec.emplace_back(spn.getY() + y, spn.getMinX() + x, spn.getMaxX() + x);
435 }
436 return std::make_shared<SpanSet>(std::move(tempVec), false);
437}
438
440 /* Return a copy of the current SpanSet but only with values which are contained within
441 * the supplied box
442 */
443 std::vector<Span> tempVec;
444 for (auto const& spn : _spanVector) {
445 if (spn.getY() >= box.getMinY() && spn.getY() <= box.getMaxY() &&
446 spansOverlap(spn, Span(spn.getY(), box.getMinX(), box.getMaxX()))) {
447 tempVec.emplace_back(spn.getY(), std::max(box.getMinX(), spn.getMinX()),
448 std::min(box.getMaxX(), spn.getMaxX()));
449 }
450 }
451 return std::make_shared<SpanSet>(std::move(tempVec), false);
452}
453
454bool SpanSet::overlaps(SpanSet const& other) const {
455 // Function to check if two SpanSets overlap
456 for (auto const& otherSpan : other) {
457 for (auto const& spn : _spanVector) {
458 if (spansOverlap(otherSpan, spn)) {
459 return true;
460 }
461 }
462 }
463 return false;
464}
465
466bool SpanSet::contains(SpanSet const& other) const {
467 // Handle null SpanSet passed as other
468 if (other.empty()) {
469 return false;
470 }
471 // Function to check if a SpanSet is entirely contained within this
472 for (auto const& otherSpn : other) {
473 std::size_t counter = 0;
474 for (auto const& spn : _spanVector) {
475 // Check that the end points of the span from other are contained in the
476 // span from this
477 if (spn.contains(lsst::geom::Point2I(otherSpn.getMinX(), otherSpn.getY())) &&
478 spn.contains(lsst::geom::Point2I(otherSpn.getMaxX(), otherSpn.getY()))) {
479 ++counter;
480 }
481 }
482 // if counter is equal to zero, then the current span from other is not
483 // contained in any span in this, and the function should return false
484 // short circuiting any other spans from other
485 if (counter == 0) {
486 return false;
487 }
488 }
489 return true;
490}
491
492bool SpanSet::contains(lsst::geom::Point2I const& point) const {
493 // Check to see if a given point is found within any spans in this
494 for (auto& spn : _spanVector) {
495 if (spn.contains(point)) {
496 return true;
497 }
498 }
499 return false;
500}
501
503 // Find the centroid of the SpanSet
504 std::size_t n = 0;
505 double xc = 0, yc = 0;
506 for (auto const& spn : _spanVector) {
507 int const y = spn.getY();
508 int const x0 = spn.getMinX();
509 int const x1 = spn.getMaxX();
510 int const npix = x1 - x0 + 1;
511
512 n += npix;
513 xc += npix * 0.5 * (x1 + x0);
514 yc += npix * y;
515 }
516 assert(n == _area);
517
518 return lsst::geom::Point2D(xc / _area, yc / _area);
519}
520
522 // Compute the shape of the SpanSet
524 double const xc = cen.getX();
525 double const yc = cen.getY();
526
527 double sumxx = 0, sumxy = 0, sumyy = 0;
528 for (auto const& spn : _spanVector) {
529 int const y = spn.getY();
530 int const x0 = spn.getX0();
531 int const x1 = spn.getX1();
532 int const npix = x1 - x0 + 1;
533
534 for (int x = x0; x <= x1; ++x) {
535 sumxx += (x - xc) * (x - xc);
536 }
537 sumxy += npix * (0.5 * (x1 + x0) - xc) * (y - yc);
538 sumyy += npix * (y - yc) * (y - yc);
539 }
540
541 return ellipses::Quadrupole(sumxx / _area, sumyy / _area, sumxy / _area);
542}
543
545 // Return a dilated SpanSet made with the given stencil, by creating a SpanSet
546 // from the stencil and forwarding to the appropriate overloaded method
547 std::shared_ptr<SpanSet> stencilToSpanSet = fromShape(r, s);
548 return dilated(*stencilToSpanSet);
549}
550
552 // Handle a null SpanSet nothing should be dilated
553 if (other.size() == 0) {
554 return std::make_shared<SpanSet>(_spanVector.begin(), _spanVector.end(), false);
555 }
556
557 // Return a dilated Spanset by the given SpanSet
558 std::vector<Span> tempVec;
559
560 for (auto const& spn : _spanVector) {
561 for (auto const& otherSpn : other) {
562 int const xmin = spn.getMinX() + otherSpn.getMinX();
563 int const xmax = spn.getMaxX() + otherSpn.getMaxX();
564 int const yval = spn.getY() + otherSpn.getY();
565 tempVec.emplace_back(yval, xmin, xmax);
566 }
567 }
568 // Allow constructor to handle merging adjacent and overlapping spans
569 return std::make_shared<SpanSet>(std::move(tempVec));
570}
571
573 // Return an eroded SpanSet made with the given stencil, by creating a SpanSet
574 // from the stencil and forwarding to the appropriate overloaded method
575 std::shared_ptr<SpanSet> stencilToSpanSet = fromShape(r, s);
576 return eroded(*stencilToSpanSet);
577}
578
580 // Handle a null SpanSet nothing should be eroded
581 if (other.size() == 0 || this->size() == 0) {
582 return std::make_shared<SpanSet>(_spanVector.begin(), _spanVector.end(), false);
583 }
584
585 // Return a SpanSet eroded by the given SpanSet
586 std::vector<Span> tempVec;
587
588 // Calculate all possible primary runs.
589 std::vector<PrimaryRun> primaryRuns;
590 for (auto const& spn : _spanVector) {
591 int m = 0;
592 for (auto const& otherSpn : other) {
593 if ((otherSpn.getMaxX() - otherSpn.getMinX()) <= (spn.getMaxX() - spn.getMinX())) {
594 int xmin = spn.getMinX() - otherSpn.getMinX();
595 int xmax = spn.getMaxX() - otherSpn.getMaxX();
596 int y = spn.getY() - otherSpn.getY();
597 primaryRuns.push_back(PrimaryRun({m, y, xmin, xmax}));
598 }
599 ++m;
600 }
601 }
602
603 // Iterate over the primary runs in such a way that we consider all values of m
604 // for a given y, then all m for y+1 etc.
605 std::sort(primaryRuns.begin(), primaryRuns.end(), comparePrimaryRun);
606
607 for (int y = primaryRuns.front().y; y <= primaryRuns.back().y; ++y) {
608 auto yRange = std::equal_range(primaryRuns.begin(), primaryRuns.end(), y, ComparePrimaryRunY());
609
610 /* Discard runs for any value of y for which we find fewer groups than M,
611 * the total Y range of the structuring element. This is step 3.1 of the
612 * Kim et al. algorithm.
613 */
614 // Plus one because end points are inclusive
615 auto otherYRange = other.back().getY() - other.front().getY() + 1;
616 if (std::distance(yRange.first, yRange.second) < otherYRange) {
617 continue;
618 }
619
620 /* "good" runs are those which are covered by each value of m, ie by each
621 * row in the structuring element. Our algorithm will consider each value
622 * of m in turn, gradually whittling down the list of good runs, then
623 * finally convert the remainder into Spans.
624 */
625 std::list<PrimaryRun> goodRuns;
626
627 for (int m = 0; m < otherYRange; ++m) {
628 auto mRange = std::equal_range(yRange.first, yRange.second, m, ComparePrimaryRunM());
629 if ((mRange.first == mRange.second)) {
630 // If a particular m is missing, we known that this y contains
631 // no good runs; this is equivalent to Kim et al. step 3.2.
632 goodRuns.clear();
633 } else {
634 // Consolidate all primary runs at this m so that they don't overlap.
635 std::list<PrimaryRun> candidateRuns;
636 int startX = mRange.first->xmin;
637 int endX = mRange.first->xmax;
638 for (auto run = mRange.first + 1; run != mRange.second; ++run) {
639 if (run->xmin > endX) {
640 // Start of a new run
641 candidateRuns.push_back(PrimaryRun{m, y, startX, endX});
642 startX = run->xmin;
643 endX = run->xmax;
644 } else {
645 // Continuation of an existing run
646 endX = run->xmax;
647 }
648 }
649 candidateRuns.push_back(PrimaryRun{m, y, startX, endX});
650
651 // Otherwise, calculate the intersection of candidate runs at
652 // this m with good runs from all previous m.
653 if (m == 0) {
654 // For m = 0 we have nothing to compare to; all runs are accepted
655 std::swap(goodRuns, candidateRuns);
656 } else {
657 std::list<PrimaryRun> newlist;
658 for (auto& good : goodRuns) {
659 for (auto& cand : candidateRuns) {
660 int start = std::max(good.xmin, cand.xmin);
661 int end = std::min(good.xmax, cand.xmax);
662 if (end >= start) {
663 newlist.push_back(PrimaryRun({m, y, start, end}));
664 }
665 }
666 }
667 std::swap(newlist, goodRuns);
668 }
669 }
670 }
671 for (auto& run : goodRuns) {
672 tempVec.emplace_back(run.y, run.xmin, run.xmax);
673 }
674 }
675 return std::make_shared<SpanSet>(std::move(tempVec));
676}
677
678bool SpanSet::operator==(SpanSet const& other) const {
679 // Check the equivalence of this SpanSet with another
680 return _spanVector == other._spanVector;
681}
682
683bool SpanSet::operator!=(SpanSet const& other) const {
684 // Check the equivalence of this SpanSet with another
685 return _spanVector != other._spanVector;
686}
687
689 // Create a SpanSet from a given Stencil
690 std::vector<Span> tempVec;
691 tempVec.reserve(2 * r + 1);
692 switch (s) {
693 case Stencil::CIRCLE:
694 for (auto dy = -r; dy <= r; ++dy) {
695 int dx = static_cast<int>(sqrt(r * r - dy * dy));
696 tempVec.emplace_back(dy + offset.getY(), -dx + offset.getX(), dx + offset.getX());
697 }
698 break;
700 for (auto dy = -r; dy <= r; ++dy) {
701 int dx = r - abs(dy);
702 tempVec.emplace_back(dy + offset.getY(), -dx + offset.getX(), dx + offset.getX());
703 }
704 break;
705 case Stencil::BOX:
706 for (auto dy = -r; dy <= r; ++dy) {
707 int dx = r;
708 tempVec.emplace_back(dy + offset.getY(), -dx + offset.getX(), dx + offset.getX());
709 }
710 break;
711 }
712 return std::make_shared<SpanSet>(std::move(tempVec), false);
713}
714
716 ellipses::PixelRegion pr(ellipse);
717 return std::make_shared<SpanSet>(pr.begin(), pr.end());
718}
719
721 // Check if the bounding boxes overlap, if not return null SpanSet
722 if (!_bbox.overlaps(other.getBBox())) {
723 return std::make_shared<SpanSet>();
724 }
725 // Handel intersecting a SpanSet with itself
726 if (other == *this) {
727 return std::make_shared<SpanSet>(this->_spanVector);
728 }
729 std::vector<Span> tempVec;
730 auto otherBeginIter = other.begin();
731 for (auto const& spn : _spanVector) {
732 while(otherBeginIter != other.end() && otherBeginIter->getY() < spn.getY()) {
733 ++otherBeginIter;
734 }
735 for (auto otherIter = otherBeginIter; otherIter != other.end() && otherIter->getY() <= spn.getY(); ++otherIter) {
736 if (spansOverlap(spn, *otherIter)) {
737 auto newMin = std::max(spn.getMinX(), otherIter->getMinX());
738 auto newMax = std::min(spn.getMaxX(), otherIter->getMaxX());
739 auto newSpan = Span(spn.getY(), newMin, newMax);
740 tempVec.push_back(newSpan);
741 }
742 }
743 }
744 return std::make_shared<SpanSet>(std::move(tempVec));
745}
746
748 // Check if the bounding boxes overlap, if not simply return a copy of this
749 if (!getBBox().overlaps(other.getBBox())) {
750 return std::make_shared<SpanSet>(this->begin(), this->end());
751 }
752 // Handle calling a SpanSet's intersectNot with itself as an argument
753 if (other == *this) {
754 return std::make_shared<SpanSet>();
755 }
756 /* This function must find all the areas in this and not in other. These SpanSets
757 * may be overlapping with this less than other a1| b1| a2| b2|,
758 * with this greater than other b1| a1| b2| a2|,
759 * with this containing other a1| b1| b2| a2|,
760 * or with other containing this b1| a1| a2| b2|
761 */
762 std::vector<Span> tempVec;
763 auto otherBeginIter = other.begin();
764 for (auto const& spn : _spanVector) {
765 bool added = false;
766 bool spanStarted = false;
767 int spanBottom = 0;
768 while(otherBeginIter != other.end() && otherBeginIter->getY() < spn.getY()) {
769 ++otherBeginIter;
770 }
771 for (auto otherIter = otherBeginIter; otherIter != other.end() && otherIter->getY() <= spn.getY(); ++otherIter) {
772 if (spansOverlap(spn, *otherIter)) {
773 added = true;
774 /* To handle one span containing the other, the spans will be added
775 * piecewise, and let the SpanSet constructor normalize spans which
776 * end up contiguous. In the case where this is contained in other,
777 * these statements will all be false, and nothing will be added,
778 * which is the expected behavior.
779 *
780 * Intersection with the logical not of another SpanSet requires that the Intersection
781 * be tested against the condition that multiple Spans may be present in the other
782 * SpanSet at the same Y coordinate. I.E. If one row in *this would contain two
783 * disconnected Spans in other, the result should be three new Spans.
784 */
785 // Check if a span is in the process of being created
786 if (!spanStarted) {
787 // If the minimum value of the Span in *this is less than that of other, add a new Span
788 if (spn.getMinX() < otherIter->getMinX()) {
789 tempVec.emplace_back(spn.getY(), spn.getMinX(), otherIter->getMinX() - 1);
790 }
791 // Conversely if the maximum value of the Span in *this is greater than that of other,
792 // Start a new span, and record what the minimum value of that span should be. This is
793 // because SpanSets can be disconnected, and the function must check if there are any
794 // additional Spans which fall in the same row
795 if (spn.getMaxX() > otherIter->getMaxX()) {
796 spanStarted = true;
797 spanBottom = otherIter->getMaxX() + 1;
798 }
799 } else {
800 // A span is in the process of being created and should be finished and added
801 tempVec.emplace_back(spn.getY(), spanBottom, std::min(spn.getMaxX(), otherIter->getMinX() - 1));
802 spanStarted = false;
803 // Check if the span in *this extends past that of the Span from other, if so
804 // begin a new Span
805 if (spn.getMaxX() > otherIter->getMaxX()) {
806 spanStarted = true;
807 spanBottom = otherIter->getMaxX() + 1;
808 }
809 }
810 }
811 if (otherIter->getMaxX() > spn.getMaxX()) {
812 break;
813 }
814 }
815 // Check if a span has been started but not finished, if that is the case that means there are
816 // no further spans on this row to consider and the span should be closed and added
817 if (spanStarted) {
818 tempVec.emplace_back(spn.getY(), spanBottom, spn.getMaxX());
819 }
820 /* If added is still zero, that means it did not overlap any of the spans in other
821 * and should be included in the new span
822 */
823 if (!added) {
824 tempVec.emplace_back(spn);
825 }
826 }
827 return std::make_shared<SpanSet>(std::move(tempVec));
828}
829
831 /* Simply include Spans from both SpanSets in a new vector and let the SpanSet
832 * constructor normalize any of the SpanSets which may be contiguous
833 */
834 std::size_t combineSize = size() + other.size();
835 std::vector<Span> tempVec;
836 tempVec.reserve(combineSize);
837 // Copy this
838 tempVec.insert(tempVec.end(), _spanVector.begin(), _spanVector.end());
839 // Copy other
840 tempVec.insert(tempVec.end(), other.begin(), other.end());
841 return std::make_shared<SpanSet>(std::move(tempVec));
842}
843
845 // Transform points in SpanSet by the LinearTransform
847}
848
850 // Transform points in SpanSet by the AffineTransform
851 return transformedBy(*makeTransform(t));
852}
853
855 // If the SpanSet is empty, its bounding box corners are not
856 // meaningful and cannot be transformed.
857 if (_spanVector.empty()) {
858 return std::make_shared<SpanSet>();
859 }
860
861 // Transform points in SpanSet by Transform<Point2Endpoint, Point2Endpoint>
862 // Transform the original bounding box
863 lsst::geom::Box2D newBBoxD;
865 fromCorners.reserve(4);
866 for (auto const& fc : _bbox.getCorners()) {
867 fromCorners.emplace_back(lsst::geom::Point2D(fc));
868 }
869 auto toPoints = t.applyForward(fromCorners);
870
871 // Check that this was a valid transformation.
872 // Inverses should be good to floating point precision, this is just
873 // a very rough check.
874 auto fromToPoints = t.applyInverse(toPoints);
875 for (int i = 0; i < 4; ++i) {
876 if ((std::abs(fromToPoints[i].getX() - fromCorners[i].getX()) > 1.0) ||
877 (std::abs(fromToPoints[i].getY() - fromCorners[i].getY()) > 1.0)) {
878 return std::make_shared<SpanSet>();
879 }
880 }
881
882 for (auto const& tc : toPoints) {
883 newBBoxD.include(tc);
884 }
885
886 lsst::geom::Box2I newBBoxI(newBBoxD);
887
889 newBoxPoints.reserve(newBBoxI.getWidth());
890 std::vector<Span> tempVec;
891 for (int y = newBBoxI.getBeginY(); y < newBBoxI.getEndY(); ++y) {
892 bool inSpan = false; // Are we in a span?
893 int start = -1; // Start of span
894
895 // vectorize one row at a time (vectorizing the whole bbox would further improve performance
896 // but could lead to memory issues for very large bounding boxes)
897 newBoxPoints.clear();
898 for (int x = newBBoxI.getBeginX(); x < newBBoxI.getEndX(); ++x) {
899 newBoxPoints.emplace_back(lsst::geom::Point2D(x, y));
900 }
901 auto oldBoxPoints = t.applyInverse(newBoxPoints);
902 auto oldBoxPointIter = oldBoxPoints.cbegin();
903 for (int x = newBBoxI.getBeginX(); x < newBBoxI.getEndX(); ++x, ++oldBoxPointIter) {
904 auto p = *oldBoxPointIter;
905 int const xSource = std::floor(0.5 + p.getX());
906 int const ySource = std::floor(0.5 + p.getY());
907
908 if (contains(lsst::geom::Point2I(xSource, ySource))) {
909 if (!inSpan) {
910 inSpan = true;
911 start = x;
912 }
913 } else if (inSpan) {
914 inSpan = false;
915 tempVec.emplace_back(y, start, x - 1);
916 }
917 }
918 if (inSpan) {
919 tempVec.emplace_back(y, start, newBBoxI.getMaxX());
920 }
921 }
922 return std::make_shared<SpanSet>(std::move(tempVec));
923}
924
925template <typename ImageT>
927 bool doClip) const {
929 if (region.isEmpty()) {
930 bbox = image.getBBox();
931 } else {
932 bbox = region;
933 }
934 auto setterFunc = [](lsst::geom::Point2I const& point, ImageT& out, ImageT in) { out = in; };
935 try {
936 if (doClip) {
937 auto tmpSpan = this->clippedTo(bbox);
938 tmpSpan->applyFunctor(setterFunc, image, val);
939 } else {
940 applyFunctor(setterFunc, image, val);
941 }
942 } catch (pex::exceptions::OutOfRangeError const&) {
944 "Footprint Bounds Outside image, set doClip to true");
945 }
946}
947
948template <typename T>
949void SpanSet::setMask(image::Mask<T>& target, T bitmask) const {
950 // Use a lambda to set bits in a mask at the locations given by SpanSet
951 auto targetArray = target.getArray();
952 auto xy0 = target.getBBox().getMin();
953 auto maskFunctor = [](lsst::geom::Point2I const& point,
955 T bitmask) { maskVal |= bitmask; };
956 applyFunctor(maskFunctor, ndarray::ndImage(targetArray, xy0), bitmask);
957}
958
959template <typename T>
960void SpanSet::clearMask(image::Mask<T>& target, T bitmask) const {
961 // Use a lambda to clear bits in a mask at the locations given by SpanSet
962 auto targetArray = target.getArray();
963 auto xy0 = target.getBBox().getMin();
964 auto clearMaskFunctor = [](lsst::geom::Point2I const& point,
966 T bitmask) { maskVal &= ~bitmask; };
967 applyFunctor(clearMaskFunctor, ndarray::ndImage(targetArray, xy0), bitmask);
968}
969
970template <typename T>
972 return maskIntersect<T, false>(*this, other, bitmask);
973}
974
975template <typename T>
977 return maskIntersect<T, true>(*this, other, bitmask);
978}
979
980template <typename T>
982 auto comparator = [bitmask](T pixelValue) { return (pixelValue & bitmask); };
983 auto spanSetFromMask = fromMask(other, comparator);
984 return union_(*spanSetFromMask);
985}
986
987namespace {
988// Singleton helper class that manages the schema and keys for the persistence of SpanSets
989class SpanSetPersistenceHelper {
990public:
991 table::Schema spanSetSchema;
992 table::Key<int> spanY;
993 table::Key<int> spanX0;
994 table::Key<int> spanX1;
995
996 static SpanSetPersistenceHelper const& get() {
997 static SpanSetPersistenceHelper instance;
998 return instance;
999 }
1000
1001 // No copying
1002 SpanSetPersistenceHelper(const SpanSetPersistenceHelper&) = delete;
1003 SpanSetPersistenceHelper& operator=(const SpanSetPersistenceHelper&) = delete;
1004
1005 // No Moving
1006 SpanSetPersistenceHelper(SpanSetPersistenceHelper&&) = delete;
1007 SpanSetPersistenceHelper& operator=(SpanSetPersistenceHelper&&) = delete;
1008
1009private:
1010 SpanSetPersistenceHelper()
1011 : spanSetSchema(),
1012 spanY(spanSetSchema.addField<int>("y", "The row of the span", "pixel")),
1013 spanX0(spanSetSchema.addField<int>("x0", "First column of span (inclusive)", "pixel")),
1014 spanX1(spanSetSchema.addField<int>("x1", "Second column of span (inclusive)", "pixel")) {}
1015};
1016
1017std::string getSpanSetPersistenceName() { return "SpanSet"; }
1018
1019class SpanSetFactory : public table::io::PersistableFactory {
1020public:
1021 std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
1022 CatalogVector const& catalogs) const override {
1023 // There should only be one catalog saved
1024 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
1025 // Get the catalog with the spans
1026 auto spansCatalog = catalogs.front();
1027 // Retrieve the keys that will be used to reference the catalog
1028 auto const& keys = SpanSetPersistenceHelper::get();
1029 // Construct a temporary container which will later be turned into the SpanSet
1030 std::vector<Span> tempVec;
1031 tempVec.reserve(spansCatalog.size());
1032 for (auto const& val : spansCatalog) {
1033 tempVec.emplace_back(val.get(keys.spanY), val.get(keys.spanX0), val.get(keys.spanX1));
1034 }
1035 return std::make_shared<SpanSet>(std::move(tempVec));
1036 }
1037 explicit SpanSetFactory(std::string const& name) : table::io::PersistableFactory(name) {}
1038};
1039
1040// insert the factory into the registry (instantiating an instance is sufficient, because the code
1041// that does the work is in the base class ctor)
1042SpanSetFactory registration(getSpanSetPersistenceName());
1043
1044} // namespace
1045
1046std::string SpanSet::getPersistenceName() const { return getSpanSetPersistenceName(); }
1047
1049 auto const& keys = SpanSetPersistenceHelper::get();
1050 auto spanCat = handle.makeCatalog(keys.spanSetSchema);
1051 spanCat.reserve(size());
1052 for (auto const& val : *this) {
1053 auto record = spanCat.addNew();
1054 record->set(keys.spanY, val.getY());
1055 record->set(keys.spanX0, val.getX0());
1056 record->set(keys.spanX1, val.getX1());
1057 }
1058 handle.saveCatalog(spanCat);
1059}
1060
1061//
1062// Explicit instantiations
1063#define INSTANTIATE_IMAGE_TYPE(T) \
1064 template void SpanSet::setImage<T>(image::Image<T> & image, T val, \
1065 lsst::geom::Box2I const& region = lsst::geom::Box2I(), \
1066 bool doClip = false) const;
1067
1068#define INSTANTIATE_MASK_TYPE(T) \
1069 template void SpanSet::setMask<T>(image::Mask<T> & target, T bitmask) const; \
1070 template void SpanSet::clearMask<T>(image::Mask<T> & target, T bitmask) const; \
1071 template std::shared_ptr<SpanSet> SpanSet::intersect<T>(image::Mask<T> const& other, T bitmask) const; \
1072 template std::shared_ptr<SpanSet> SpanSet::intersectNot<T>(image::Mask<T> const& other, T bitmask) \
1073 const; \
1074 template std::shared_ptr<SpanSet> SpanSet::union_<T>(image::Mask<T> const& other, T bitmask) const;
1075
1081
1083} // namespace geom
1084} // namespace afw
1085} // namespace lsst
AmpInfoBoxKey bbox
Definition Amplifier.cc:117
Key< Flag > const & target
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
table::Key< int > spanX1
Definition Footprint.cc:210
table::Key< int > spanY
Definition Footprint.cc:208
table::Key< int > spanX0
Definition Footprint.cc:209
afw::table::Key< afw::table::Array< MaskPixelT > > mask
int y
Definition SpanSet.cc:48
int xmax
Definition SpanSet.cc:48
#define INSTANTIATE_MASK_TYPE(T)
Definition SpanSet.cc:1068
int m
Definition SpanSet.cc:48
int xmin
Definition SpanSet.cc:48
#define INSTANTIATE_IMAGE_TYPE(T)
Definition SpanSet.cc:1063
table::Schema spanSetSchema
Definition SpanSet.cc:991
table::Key< int > b
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition Persistable.h:48
T back(T... args)
T begin(T... args)
A range of pixels within one row of an Image.
Definition Span.h:47
int getMinX() const noexcept
Minimum x-value.
Definition Span.h:84
int getMaxX() const noexcept
Maximum x-value.
Definition Span.h:85
bool contains(int x) const noexcept
Definition Span.h:95
int getY() const noexcept
Return the y-value.
Definition Span.h:81
int getX0() const noexcept
Return the starting x-value.
Definition Span.h:76
int getX1() const noexcept
Return the ending x-value.
Definition Span.h:78
A compact representation of a collection of pixels.
Definition SpanSet.h:78
bool isContiguous() const
Defines if the SpanSet is simply contiguous.
Definition SpanSet.cc:373
SpanSet()
Default constructor.
Definition SpanSet.cc:177
std::shared_ptr< SpanSet > shiftedBy(int x, int y) const
Return a new SpanSet shifted by specified amount.
Definition SpanSet.cc:419
static std::shared_ptr< geom::SpanSet > fromMask(image::Mask< T > const &mask, UnaryPredicate comparator=details::AnyBitSetFunctor< T >())
Create a SpanSet from a mask.
Definition SpanSet.h:644
static std::shared_ptr< geom::SpanSet > fromShape(int r, Stencil s=Stencil::CIRCLE, lsst::geom::Point2I offset=lsst::geom::Point2I())
Factory function for creating SpanSets from a Stencil.
Definition SpanSet.cc:688
void setImage(image::Image< ImageT > &image, ImageT val, lsst::geom::Box2I const &region=lsst::geom::Box2I(), bool doClip=false) const
Set the values of an Image at points defined by the SpanSet.
Definition SpanSet.cc:926
std::shared_ptr< SpanSet > union_(SpanSet const &other) const
Create a new SpanSet that contains all points from two SpanSets.
Definition SpanSet.cc:830
std::shared_ptr< SpanSet > transformedBy(lsst::geom::LinearTransform const &t) const
Return a new SpanSet who's pixels are the product of applying the specified transformation.
Definition SpanSet.cc:844
std::shared_ptr< SpanSet > clippedTo(lsst::geom::Box2I const &box) const
Return a new SpanSet which has all pixel values inside specified box.
Definition SpanSet.cc:439
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
Definition SpanSet.cc:1048
const_iterator end() const
Definition SpanSet.h:89
std::shared_ptr< SpanSet > intersect(SpanSet const &other) const
Determine the common points between two SpanSets, and create a new SpanSet.
Definition SpanSet.cc:720
void applyFunctor(Functor &&func, Args &&... args) const
Apply functor on individual elements from the supplied parameters.
Definition SpanSet.h:512
std::shared_ptr< SpanSet > eroded(int r, Stencil s=Stencil::CIRCLE) const
Perform a set erosion, and return a new object.
Definition SpanSet.cc:572
lsst::geom::Box2I getBBox() const
Return a new integer box which is the minimum size to contain the pixels.
Definition SpanSet.cc:286
std::shared_ptr< SpanSet > dilated(int r, Stencil s=Stencil::CIRCLE) const
Perform a set dilation operation, and return a new object.
Definition SpanSet.cc:544
const_reference front() const
Definition SpanSet.h:92
size_type size() const
Definition SpanSet.h:94
void setMask(lsst::afw::image::Mask< T > &target, T bitmask) const
Set a Mask at pixels defined by the SpanSet.
Definition SpanSet.cc:949
std::vector< std::shared_ptr< geom::SpanSet > > split() const
Split a discontinuous SpanSet into multiple SpanSets which are contiguous.
Definition SpanSet.cc:382
bool empty() const
Definition SpanSet.h:95
bool overlaps(SpanSet const &other) const
Specifies if this SpanSet overlaps with another SpanSet.
Definition SpanSet.cc:454
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
Definition SpanSet.cc:1046
const_iterator begin() const
Definition SpanSet.h:88
bool operator!=(SpanSet const &other) const
Definition SpanSet.cc:683
void clearMask(lsst::afw::image::Mask< T > &target, T bitmask) const
Unset a Mask at pixels defined by the SpanSet.
Definition SpanSet.cc:960
lsst::geom::Point2D computeCentroid() const
Compute the point about which the SpanSet's first moment is zero.
Definition SpanSet.cc:502
bool contains(SpanSet const &other) const
Check if a SpanSet instance entirely contains another SpanSet.
Definition SpanSet.cc:466
std::shared_ptr< geom::SpanSet > findEdgePixels() const
Select pixels within the SpanSet which touch its edge.
Definition SpanSet.cc:411
ellipses::Quadrupole computeShape() const
Compute the shape parameters for the distribution of points in the SpanSet.
Definition SpanSet.cc:521
bool operator==(SpanSet const &other) const
Compute equality between two SpanSets.
Definition SpanSet.cc:678
std::shared_ptr< SpanSet > intersectNot(SpanSet const &other) const
Determine the common points between a SpanSet and the logical inverse of a second SpanSet and return ...
Definition SpanSet.cc:747
const_reference back() const
Definition SpanSet.h:93
size_type getArea() const
Return the number of pixels in the SpanSet.
Definition SpanSet.cc:283
Transform LSST spatial data, such as lsst::geom::Point2D and lsst::geom::SpherePoint,...
Definition Transform.h:68
FromPoint applyInverse(ToPoint const &point) const
Transform one point in the inverse direction ("to" to "from")
ToPoint applyForward(FromPoint const &point) const
Transform one point in the forward direction ("from" to "to")
typename ndarray::Array< T, N, C >::Reference::Reference Reference
An ellipse defined by an arbitrary BaseCore and a center point.
Definition Ellipse.h:51
A pixelized region containing all pixels whose centers are within an Ellipse.
Definition PixelRegion.h:46
Iterator begin() const
Iterator range over Spans whose pixels are within the Ellipse.
Definition PixelRegion.h:68
An ellipse core with quadrupole moments as parameters.
Definition Quadrupole.h:47
A class to represent a 2-dimensional array of pixels.
Definition Image.h:51
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:82
void reserve(size_type n)
Increase the capacity of the catalog to the given size.
Definition Catalog.h:432
An object passed to Persistable::write to allow it to persist itself.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
An affine coordinate transformation consisting of a linear transformation and an offset.
A floating-point coordinate rectangle geometry.
Definition Box.h:413
void include(Point2D const &point) noexcept
Expand this to ensure that this->contains(point).
Definition Box.cc:380
An integer coordinate rectangle.
Definition Box.h:55
int getBeginX() const noexcept
Definition Box.h:172
int getMinY() const noexcept
Definition Box.h:158
bool overlaps(Box2I const &other) const noexcept
Return true if any points in other are also in this.
Definition Box.cc:122
bool isEmpty() const noexcept
Return true if the box contains no points.
Definition Box.h:213
int getEndY() const noexcept
Definition Box.h:177
int getMinX() const noexcept
Definition Box.h:157
int getWidth() const noexcept
Definition Box.h:187
int getBeginY() const noexcept
Definition Box.h:173
int getMaxX() const noexcept
Definition Box.h:161
std::vector< Point2I > getCorners() const
Get the corner points.
Definition Box.cc:261
int getMaxY() const noexcept
Definition Box.h:162
int getEndX() const noexcept
Definition Box.h:176
A 2D linear coordinate transformation.
Reports attempts to access elements outside a valid range of indices.
Definition Runtime.h:89
T clear(T... args)
T distance(T... args)
T emplace_back(T... args)
T end(T... args)
T equal_range(T... args)
T floor(T... args)
T front(T... args)
T insert(T... args)
T make_pair(T... args)
T max(T... args)
T min(T... args)
T move(T... args)
std::shared_ptr< TransformPoint2ToPoint2 > makeTransform(lsst::geom::AffineTransform const &affine)
Wrap an lsst::geom::AffineTransform as a Transform.
Stencil
An enumeration class which describes the shapes.
Definition SpanSet.h:66
Point< double, 2 > Point2D
Definition Point.h:324
details::ImageNdGetter< T, inA, inB > ndImage(ndarray::Array< T, inA, inB > const &array, lsst::geom::Point2I xy0=lsst::geom::Point2I())
Marks a ndarray to be interpreted as an image when applying a functor from a SpanSet.
STL namespace.
T push_back(T... args)
T reserve(T... args)
T sort(T... args)
ImageT val
Definition CR.cc:146
T swap(T... args)
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override