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