LSST Applications g070148d5b3+33e5256705,g0d53e28543+25c8b88941,g0da5cf3356+2dd1178308,g1081da9e2a+62d12e78cb,g17e5ecfddb+7e422d6136,g1c76d35bf8+ede3a706f7,g295839609d+225697d880,g2e2c1a68ba+cc1f6f037e,g2ffcdf413f+853cd4dcde,g38293774b4+62d12e78cb,g3b44f30a73+d953f1ac34,g48ccf36440+885b902d19,g4b2f1765b6+7dedbde6d2,g5320a0a9f6+0c5d6105b6,g56b687f8c9+ede3a706f7,g5c4744a4d9+ef6ac23297,g5ffd174ac0+0c5d6105b6,g6075d09f38+66af417445,g667d525e37+2ced63db88,g670421136f+2ced63db88,g71f27ac40c+2ced63db88,g774830318a+463cbe8d1f,g7876bc68e5+1d137996f1,g7985c39107+62d12e78cb,g7fdac2220c+0fd8241c05,g96f01af41f+368e6903a7,g9ca82378b8+2ced63db88,g9d27549199+ef6ac23297,gabe93b2c52+e3573e3735,gb065e2a02a+3dfbe639da,gbc3249ced9+0c5d6105b6,gbec6a3398f+0c5d6105b6,gc9534b9d65+35b9f25267,gd01420fc67+0c5d6105b6,geee7ff78d7+a14128c129,gf63283c776+ede3a706f7,gfed783d017+0c5d6105b6,w.2022.47
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 otherIter = other.begin();
731 for (auto const& spn : _spanVector) {
732 while (otherIter != other.end() && otherIter->getY() <= spn.getY()) {
733 if (spansOverlap(spn, *otherIter)) {
734 auto newMin = std::max(spn.getMinX(), otherIter->getMinX());
735 auto newMax = std::min(spn.getMaxX(), otherIter->getMaxX());
736 auto newSpan = Span(spn.getY(), newMin, newMax);
737 tempVec.push_back(newSpan);
738 }
739 ++otherIter;
740 }
741 }
742 return std::make_shared<SpanSet>(std::move(tempVec));
743}
744
746 // Check if the bounding boxes overlap, if not simply return a copy of this
747 if (!getBBox().overlaps(other.getBBox())) {
748 return std::make_shared<SpanSet>(this->begin(), this->end());
749 }
750 // Handle calling a SpanSet's intersectNot with itself as an argument
751 if (other == *this) {
752 return std::make_shared<SpanSet>();
753 }
754 /* This function must find all the areas in this and not in other. These SpanSets
755 * may be overlapping with this less than other a1| b1| a2| b2|,
756 * with this greater than other b1| a1| b2| a2|,
757 * with this containing other a1| b1| b2| a2|,
758 * or with other containing this b1| a1| a2| b2|
759 */
760 std::vector<Span> tempVec;
761 auto otherIter = other.begin();
762 for (auto const& spn : _spanVector) {
763 bool added = false;
764 bool spanStarted = false;
765 int spanBottom = 0;
766 while (otherIter != other.end() && otherIter->getY() <= spn.getY()) {
767 if (spansOverlap(spn, *otherIter)) {
768 added = true;
769 /* To handle one span containing the other, the spans will be added
770 * piecewise, and let the SpanSet constructor normalize spans which
771 * end up contiguous. In the case where this is contained in other,
772 * these statements will all be false, and nothing will be added,
773 * which is the expected behavior.
774 *
775 * Intersection with the logical not of another SpanSet requires that the Intersection
776 * be tested against the condition that multiple Spans may be present in the other
777 * SpanSet at the same Y coordinate. I.E. If one row in *this would contain two
778 * disconnected Spans in other, the result should be three new Spans.
779 */
780 // Check if a span is in the process of being created
781 if (!spanStarted) {
782 // If the minimum value of the Span in *this is less than that of other, add a new Span
783 if (spn.getMinX() < otherIter->getMinX()) {
784 tempVec.emplace_back(spn.getY(), spn.getMinX(), otherIter->getMinX() - 1);
785 }
786 // Conversely if the maximum value of the Span in *this is greater than that of other,
787 // Start a new span, and record what the minimum value of that span should be. This is
788 // because SpanSets can be disconnected, and the function must check if there are any
789 // additional Spans which fall in the same row
790 if (spn.getMaxX() > otherIter->getMaxX()) {
791 spanStarted = true;
792 spanBottom = otherIter->getMaxX() + 1;
793 }
794 } else {
795 // A span is in the process of being created and should be finished and added
796 tempVec.emplace_back(spn.getY(), spanBottom, std::min(spn.getMaxX(), otherIter->getMinX() - 1));
797 spanStarted = false;
798 // Check if the span in *this extends past that of the Span from other, if so
799 // begin a new Span
800 if (spn.getMaxX() > otherIter->getMaxX()) {
801 spanStarted = true;
802 spanBottom = otherIter->getMaxX() + 1;
803 }
804 }
805 }
806 if (otherIter->getMaxX() > spn.getMaxX()) {
807 break;
808 }
809 ++otherIter;
810 }
811 // Check if a span has been started but not finished, if that is the case that means there are
812 // no further spans on this row to consider and the span should be closed and added
813 if (spanStarted) {
814 tempVec.emplace_back(spn.getY(), spanBottom, spn.getMaxX());
815 }
816 /* If added is still zero, that means it did not overlap any of the spans in other
817 * and should be included in the new span
818 */
819 if (!added) {
820 tempVec.emplace_back(spn);
821 }
822 }
823 return std::make_shared<SpanSet>(std::move(tempVec));
824}
825
827 /* Simply include Spans from both SpanSets in a new vector and let the SpanSet
828 * constructor normalize any of the SpanSets which may be contiguous
829 */
830 std::size_t combineSize = size() + other.size();
831 std::vector<Span> tempVec;
832 tempVec.reserve(combineSize);
833 // Copy this
834 tempVec.insert(tempVec.end(), _spanVector.begin(), _spanVector.end());
835 // Copy other
836 tempVec.insert(tempVec.end(), other.begin(), other.end());
837 return std::make_shared<SpanSet>(std::move(tempVec));
838}
839
841 // Transform points in SpanSet by the LinearTransform
843}
844
846 // Transform points in SpanSet by the AffineTransform
847 return transformedBy(*makeTransform(t));
848}
849
851 // Transform points in SpanSet by Transform<Point2Endpoint, Point2Endpoint>
852 // Transform the original bounding box
853 lsst::geom::Box2D newBBoxD;
855 fromCorners.reserve(4);
856 for (auto const& fc : _bbox.getCorners()) {
857 fromCorners.emplace_back(lsst::geom::Point2D(fc));
858 }
859 auto toPoints = t.applyForward(fromCorners);
860 for (auto const& tc : toPoints) {
861 newBBoxD.include(tc);
862 }
863
864 lsst::geom::Box2I newBBoxI(newBBoxD);
865
867 newBoxPoints.reserve(newBBoxI.getWidth());
868 std::vector<Span> tempVec;
869 for (int y = newBBoxI.getBeginY(); y < newBBoxI.getEndY(); ++y) {
870 bool inSpan = false; // Are we in a span?
871 int start = -1; // Start of span
872
873 // vectorize one row at a time (vectorizing the whole bbox would further improve performance
874 // but could lead to memory issues for very large bounding boxes)
875 newBoxPoints.clear();
876 for (int x = newBBoxI.getBeginX(); x < newBBoxI.getEndX(); ++x) {
877 newBoxPoints.emplace_back(lsst::geom::Point2D(x, y));
878 }
879 auto oldBoxPoints = t.applyInverse(newBoxPoints);
880 auto oldBoxPointIter = oldBoxPoints.cbegin();
881 for (int x = newBBoxI.getBeginX(); x < newBBoxI.getEndX(); ++x, ++oldBoxPointIter) {
882 auto p = *oldBoxPointIter;
883 int const xSource = std::floor(0.5 + p.getX());
884 int const ySource = std::floor(0.5 + p.getY());
885
886 if (contains(lsst::geom::Point2I(xSource, ySource))) {
887 if (!inSpan) {
888 inSpan = true;
889 start = x;
890 }
891 } else if (inSpan) {
892 inSpan = false;
893 tempVec.emplace_back(y, start, x - 1);
894 }
895 }
896 if (inSpan) {
897 tempVec.emplace_back(y, start, newBBoxI.getMaxX());
898 }
899 }
900 return std::make_shared<SpanSet>(std::move(tempVec));
901}
902
903template <typename ImageT>
905 bool doClip) const {
907 if (region.isEmpty()) {
908 bbox = image.getBBox();
909 } else {
910 bbox = region;
911 }
912 auto setterFunc = [](lsst::geom::Point2I const& point, ImageT& out, ImageT in) { out = in; };
913 try {
914 if (doClip) {
915 auto tmpSpan = this->clippedTo(bbox);
916 tmpSpan->applyFunctor(setterFunc, image, val);
917 } else {
918 applyFunctor(setterFunc, image, val);
919 }
920 } catch (pex::exceptions::OutOfRangeError const&) {
922 "Footprint Bounds Outside image, set doClip to true");
923 }
924}
925
926template <typename T>
927void SpanSet::setMask(image::Mask<T>& target, T bitmask) const {
928 // Use a lambda to set bits in a mask at the locations given by SpanSet
929 auto targetArray = target.getArray();
930 auto xy0 = target.getBBox().getMin();
931 auto maskFunctor = [](lsst::geom::Point2I const& point,
933 T bitmask) { maskVal |= bitmask; };
934 applyFunctor(maskFunctor, ndarray::ndImage(targetArray, xy0), bitmask);
935}
936
937template <typename T>
938void SpanSet::clearMask(image::Mask<T>& target, T bitmask) const {
939 // Use a lambda to clear bits in a mask at the locations given by SpanSet
940 auto targetArray = target.getArray();
941 auto xy0 = target.getBBox().getMin();
942 auto clearMaskFunctor = [](lsst::geom::Point2I const& point,
944 T bitmask) { maskVal &= ~bitmask; };
945 applyFunctor(clearMaskFunctor, ndarray::ndImage(targetArray, xy0), bitmask);
946}
947
948template <typename T>
950 return maskIntersect<T, false>(*this, other, bitmask);
951}
952
953template <typename T>
955 return maskIntersect<T, true>(*this, other, bitmask);
956}
957
958template <typename T>
960 auto comparator = [bitmask](T pixelValue) { return (pixelValue & bitmask); };
961 auto spanSetFromMask = fromMask(other, comparator);
962 return union_(*spanSetFromMask);
963}
964
965namespace {
966// Singleton helper class that manages the schema and keys for the persistence of SpanSets
967class SpanSetPersistenceHelper {
968public:
969 table::Schema spanSetSchema;
970 table::Key<int> spanY;
971 table::Key<int> spanX0;
972 table::Key<int> spanX1;
973
974 static SpanSetPersistenceHelper const& get() {
975 static SpanSetPersistenceHelper instance;
976 return instance;
977 }
978
979 // No copying
980 SpanSetPersistenceHelper(const SpanSetPersistenceHelper&) = delete;
981 SpanSetPersistenceHelper& operator=(const SpanSetPersistenceHelper&) = delete;
982
983 // No Moving
984 SpanSetPersistenceHelper(SpanSetPersistenceHelper&&) = delete;
985 SpanSetPersistenceHelper& operator=(SpanSetPersistenceHelper&&) = delete;
986
987private:
988 SpanSetPersistenceHelper()
989 : spanSetSchema(),
990 spanY(spanSetSchema.addField<int>("y", "The row of the span", "pixel")),
991 spanX0(spanSetSchema.addField<int>("x0", "First column of span (inclusive)", "pixel")),
992 spanX1(spanSetSchema.addField<int>("x1", "Second column of span (inclusive)", "pixel")) {}
993};
994
995std::string getSpanSetPersistenceName() { return "SpanSet"; }
996
997class SpanSetFactory : public table::io::PersistableFactory {
998public:
999 std::shared_ptr<table::io::Persistable> read(InputArchive const& archive,
1000 CatalogVector const& catalogs) const override {
1001 // There should only be one catalog saved
1002 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
1003 // Get the catalog with the spans
1004 auto spansCatalog = catalogs.front();
1005 // Retrieve the keys that will be used to reference the catalog
1006 auto const& keys = SpanSetPersistenceHelper::get();
1007 // Construct a temporary container which will later be turned into the SpanSet
1008 std::vector<Span> tempVec;
1009 tempVec.reserve(spansCatalog.size());
1010 for (auto const& val : spansCatalog) {
1011 tempVec.emplace_back(val.get(keys.spanY), val.get(keys.spanX0), val.get(keys.spanX1));
1012 }
1013 return std::make_shared<SpanSet>(std::move(tempVec));
1014 }
1015 explicit SpanSetFactory(std::string const& name) : table::io::PersistableFactory(name) {}
1016};
1017
1018// insert the factory into the registry (instantiating an instance is sufficient, because the code
1019// that does the work is in the base class ctor)
1020SpanSetFactory registration(getSpanSetPersistenceName());
1021
1022} // namespace
1023
1024std::string SpanSet::getPersistenceName() const { return getSpanSetPersistenceName(); }
1025
1026void SpanSet::write(OutputArchiveHandle& handle) const {
1027 auto const& keys = SpanSetPersistenceHelper::get();
1028 auto spanCat = handle.makeCatalog(keys.spanSetSchema);
1029 spanCat.reserve(size());
1030 for (auto const& val : *this) {
1031 auto record = spanCat.addNew();
1032 record->set(keys.spanY, val.getY());
1033 record->set(keys.spanX0, val.getX0());
1034 record->set(keys.spanX1, val.getX1());
1035 }
1036 handle.saveCatalog(spanCat);
1037}
1038
1039//
1040// Explicit instantiations
1041#define INSTANTIATE_IMAGE_TYPE(T) \
1042 template void SpanSet::setImage<T>(image::Image<T> & image, T val, \
1043 lsst::geom::Box2I const& region = lsst::geom::Box2I(), \
1044 bool doClip = false) const;
1045
1046#define INSTANTIATE_MASK_TYPE(T) \
1047 template void SpanSet::setMask<T>(image::Mask<T> & target, T bitmask) const; \
1048 template void SpanSet::clearMask<T>(image::Mask<T> & target, T bitmask) const; \
1049 template std::shared_ptr<SpanSet> SpanSet::intersect<T>(image::Mask<T> const& other, T bitmask) const; \
1050 template std::shared_ptr<SpanSet> SpanSet::intersectNot<T>(image::Mask<T> const& other, T bitmask) \
1051 const; \
1052 template std::shared_ptr<SpanSet> SpanSet::union_<T>(image::Mask<T> const& other, T bitmask) const;
1053
1059
1061} // namespace geom
1062} // namespace afw
1063} // namespace lsst
table::Key< std::string > name
Definition: Amplifier.cc:116
AmpInfoBoxKey bbox
Definition: Amplifier.cc:117
Key< Flag > const & target
double x
#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:1046
int m
Definition: SpanSet.cc:48
int xmin
Definition: SpanSet.cc:48
#define INSTANTIATE_IMAGE_TYPE(T)
Definition: SpanSet.cc:1041
table::Schema spanSetSchema
Definition: SpanSet.cc:969
table::Key< int > b
table::Key< int > a
#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:904
std::shared_ptr< SpanSet > union_(SpanSet const &other) const
Create a new SpanSet that contains all points from two SpanSets.
Definition: SpanSet.cc:826
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:840
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
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:927
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
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:938
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:745
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:77
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
Definition: Persistable.cc:18
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
Definition: warpExposure.cc:0