LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
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>
26 #include "lsst/afw/geom/SpanSet.h"
33 
34 namespace lsst {
35 namespace afw {
36 
39 
40 namespace geom {
41 namespace {
42 
43 /* These classes are used in the erode operator to quickly calculate the
44  * contents of the shrunken SpanSet
45  */
46 
47 struct PrimaryRun {
48  int m, y, xmin, xmax;
49 };
50 
51 bool 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 
61 class ComparePrimaryRunY {
62 public:
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 
67 class ComparePrimaryRunM {
68 public:
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  */
79 bool 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  */
97 bool 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 */
119 template <typename T, bool invert>
120 std::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
177 SpanSet::SpanSet() : _spanVector(), _bbox(), _area(0) {}
178 
179 // Construct a SpanSet from an lsst::geom::Box2I object
180 SpanSet::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
192 SpanSet::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
206 SpanSet::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 
219 void 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 
252 void 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
283 std::size_t SpanSet::getArea() const { return _area; }
284 
285 // Getter for the bounding box of the SpanSet
286 lsst::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 
321 void 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 
345 std::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 
373 bool SpanSet::isContiguous() const {
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 
429 std::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 
454 bool 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 
466 bool 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 
492 bool 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 
678 bool SpanSet::operator==(SpanSet const& other) const {
679  // Check the equivalence of this SpanSet with another
680  return _spanVector == other._spanVector;
681 }
682 
683 bool 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;
699  case Stencil::MANHATTAN:
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 
903 template <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 
926 template <typename T>
927 void 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 
937 template <typename T>
938 void 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 
948 template <typename T>
950  return maskIntersect<T, false>(*this, other, bitmask);
951 }
952 
953 template <typename T>
955  return maskIntersect<T, true>(*this, other, bitmask);
956 }
957 
958 template <typename T>
960  auto comparator = [bitmask](T pixelValue) { return (pixelValue & bitmask); };
961  auto spanSetFromMask = fromMask(other, comparator);
962  return union_(*spanSetFromMask);
963 }
964 
965 namespace {
966 // Singleton helper class that manages the schema and keys for the persistence of SpanSets
967 class SpanSetPersistenceHelper {
968 public:
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 
987 private:
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 
995 std::string getSpanSetPersistenceName() { return "SpanSet"; }
996 
997 class SpanSetFactory : public table::io::PersistableFactory {
998 public:
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)
1020 SpanSetFactory registration(getSpanSetPersistenceName());
1021 
1022 } // namespace
1023 
1024 std::string SpanSet::getPersistenceName() const { return getSpanSetPersistenceName(); }
1025 
1026 void 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 
1057 INSTANTIATE_IMAGE_TYPE(float);
1058 INSTANTIATE_IMAGE_TYPE(double);
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
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
table::Key< int > spanX1
Definition: SpanSet.cc:972
#define INSTANTIATE_IMAGE_TYPE(T)
Definition: SpanSet.cc:1041
table::Key< int > spanY
Definition: SpanSet.cc:970
table::Schema spanSetSchema
Definition: SpanSet.cc:969
table::Key< int > spanX0
Definition: SpanSet.cc:971
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 > 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
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
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
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
FilterProperty & operator=(FilterProperty const &)=default
Point< double, 2 > Point2D
Definition: Point.h:324
def run(self, coaddExposures, bbox, wcs)
Definition: getTemplate.py:603
Angle abs(Angle const &a)
Definition: Angle.h:106
Relationship invert(Relationship r)
Given the relationship between two sets A and B (i.e.
Definition: Relationship.h:55
A base class for image defects.
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