LSSTApplications  1.1.2+25,10.0+13,10.0+132,10.0+133,10.0+224,10.0+41,10.0+8,10.0-1-g0f53050+14,10.0-1-g4b7b172+19,10.0-1-g61a5bae+98,10.0-1-g7408a83+3,10.0-1-gc1e0f5a+19,10.0-1-gdb4482e+14,10.0-11-g3947115+2,10.0-12-g8719d8b+2,10.0-15-ga3f480f+1,10.0-2-g4f67435,10.0-2-gcb4bc6c+26,10.0-28-gf7f57a9+1,10.0-3-g1bbe32c+14,10.0-3-g5b46d21,10.0-4-g027f45f+5,10.0-4-g86f66b5+2,10.0-4-gc4fccf3+24,10.0-40-g4349866+2,10.0-5-g766159b,10.0-5-gca2295e+25,10.0-6-g462a451+1
LSSTDataManagementBasePackage
ImageUtils.cc
Go to the documentation of this file.
1 // -*- lsst-c++ -*-
2 
3 /*
4  * LSST Data Management System
5  * Copyright 2008, 2009, 2010 LSST Corporation.
6  *
7  * This product includes software developed by the
8  * LSST Project (http://www.lsst.org/).
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the LSST License Statement and
21  * the GNU General Public License along with this program. If not,
22  * see <http://www.lsstcorp.org/LegalNotices/>.
23  */
24 
32 
33 #include <algorithm>
34 #include <limits>
35 #include <utility>
36 
37 #include "lsst/pex/exceptions.h"
38 #include "lsst/afw/geom/Point.h"
39 
40 
41 namespace except = lsst::pex::exceptions;
42 namespace table = lsst::afw::table;
43 namespace image = lsst::afw::image;
44 
46 
47 
48 namespace lsst { namespace ap { namespace utils {
49 
50 namespace {
51 
52 double orient2d(Point2D const &a,
53  Point2D const &b,
54  Point2D const &c)
55 {
56  double detLeft = (a.getX() - c.getX()) * (b.getY() - c.getY());
57  double detRight = (a.getY() - c.getY()) * (b.getX() - c.getX());
58  double det = detLeft - detRight;
59  double detSum;
60 
61  if (detLeft > 0.0) {
62  if (detRight <= 0.0) {
63  return det;
64  } else {
65  detSum = detLeft + detRight;
66  }
67  } else if (detLeft < 0.0) {
68  if (detRight >= 0.0) {
69  return det;
70  } else {
71  detSum = -detLeft - detRight;
72  }
73  } else {
74  return det;
75  }
76  double epsilon = std::numeric_limits<double>::epsilon();
77  double errBound = detSum * ((3.0 + 16.0 * epsilon) * epsilon);
78  if (det >= errBound || -det >= errBound) {
79  return det;
80  }
81  return 0.0;
82 }
83 
87 struct Edge {
88  Point2D const *v1;
89  Point2D const *v2;
90  double dx, dy;
91  Edge *next;
92 
93  Edge(Point2D const & p1,
94  Point2D const & p2) :
95  v1(p1.getY() < p2.getY() ? &p1 : &p2),
96  v2(p1.getY() < p2.getY() ? &p2 : &p1),
97  next(0)
98  {
99  dy = v2->getY() - v1->getY();
100  dx = v2->getX() - v1->getX();
101  }
102 
103  // edges are compared by minimum y
104  bool operator==(Edge const & e) const {
105  return v1->getY() == e.v1->getY();
106  }
107  bool operator<(Edge const & e) const {
108  return v1->getY() < e.v1->getY();
109  }
110 
113  bool rightOf(Edge const &e) const {
114  double det;
115  if (v1 == e.v1) {
116  det = orient2d(*e.v1, *e.v2, *v2);
117  } else if (v2 == e.v2) {
118  det = orient2d(*e.v1, *e.v2, *v1);
119  } else {
120  det = orient2d(*e.v1, *e.v2, *v1);
121  if (det == 0.0) {
122  det = orient2d(*e.v1, *e.v2, *v2);
123  }
124  }
125  return det < 0.0;
126  }
127 
131  std::pair<int, int> const getXBounds(double yMin, double yMax, int width) const {
132  double xd1 = v1->getX() + ((yMin - v1->getY()) * dx) / dy;
133  double xd2 = v1->getX() + ((yMax - v1->getY()) * dx) / dy;
134  int x1 = width, x2 = width;
135  if (dx > 0.0) {
136  xd1 = xd1 < v1->getX() ? v1->getX() : (xd1 > v2->getX() ? v2->getX() : xd1);
137  xd2 = xd2 < xd1 ? xd1 : (xd2 > v2->getX() ? v2->getX() : xd2);
138  if (xd1 < image::PixelZeroPos - 0.5 + width) {
139  x1 = (xd1 < image::PixelZeroPos - 0.5) ? 0 : image::positionToIndex(xd1);
140  }
141  if (xd2 < image::PixelZeroPos - 0.5 + width) {
142  x2 = (xd2 < image::PixelZeroPos - 0.5) ? 0 : image::positionToIndex(xd2) + 1;
143  }
144  } else {
145  xd2 = xd2 < v2->getX() ? v2->getX() : (xd2 > v1->getX() ? v1->getX() : xd2);
146  xd1 = xd1 < xd2 ? xd2 : (xd1 > v1->getX() ? v1->getX() : xd1);
147  if (xd2 < image::PixelZeroPos - 0.5 + width) {
148  x1 = (xd2 < image::PixelZeroPos - 0.5) ? 0 : image::positionToIndex(xd2);
149  }
150  if (xd1 < image::PixelZeroPos - 0.5 + width) {
151  x2 = (xd1 < image::PixelZeroPos - 0.5) ? 0 : image::positionToIndex(xd1) + 1;
152  }
153  }
154  return std::make_pair(std::min(x1, x2), std::max(x1, x2));
155  }
156 
160  double getCoverage(double const yMin, double const yMax, int x) const;
161 };
162 
163 
164 double Edge::getCoverage(double const yMin, double const yMax, int x) const {
165  double const xp = image::indexToPosition(x);
166  double const xMin = xp - 0.5;
167  double const xMax = xp + 0.5;
168  double cov = 0.0;
169  if (dx == 0.0) { // vertical edge
170  return (yMax - yMin) * (xMax - v1->getX());
171  } else if (dx > 0.0) { // edge with positive slope
172  double x1 = xMin;
173  // compute intersection with left pixel boundary
174  double y1 = v1->getY() + ((xMin - v1->getX()) * dy) / dx;
175  if (y1 > yMin) {
176  if (y1 >= yMax) {
177  return yMax - yMin;
178  }
179  cov = y1 - yMin;
180  } else {
181  double x = v1->getX() + ((yMin - v1->getY()) * dx) / dy;
182  x1 = (x < xMin) ? xMin : (x > xMax) ? xMax : x;
183  y1 = yMin;
184  }
185  double x2 = xMax;
186  // compute intersection with right pixel boundary
187  double y2 = v1->getY() + ((xMax - v1->getX()) * dy) / dx;
188  if (y2 < yMax) {
189  if (y2 <= yMin) {
190  return 0.0;
191  }
192  } else {
193  double x = v1->getX() + ((yMax - v1->getY()) * dx) / dy;
194  x2 = (x < xMin) ? xMin : (x > xMax) ? xMax : x;
195  y2 = yMax;
196  }
197  if (y1 > y2) {
198  y1 = y2;
199  }
200  if (x1 > x2) {
201  x1 = x2;
202  }
203  cov += (xMax - x2 + 0.5 * (x2 - x1)) * (y2 - y1);
204  } else { // edge with negative slope
205  double x1 = xMax;
206  // compute intersection with right pixel boundary
207  double y1 = v1->getY() + ((xMax - v1->getX()) * dy) / dx;
208  if (y1 > yMin) {
209  if (y1 >= yMax) {
210  return 0.0;
211  }
212  } else {
213  double x = v1->getX() + ((yMin - v1->getY()) * dx) / dy;
214  x1 = (x < xMin) ? xMin : (x > xMax) ? xMax : x;
215  y1 = yMin;
216  }
217  double x2 = xMin;
218  // compute intersection with left pixel boundary
219  double y2 = v1->getY() + ((xMin - v1->getX()) * dy) / dx;
220  if (y2 < yMax) {
221  if (y2 <= yMin) {
222  return yMax - yMin;
223  }
224  cov = yMax - y2;
225  } else {
226  double x = v1->getX() + ((yMax - v1->getY()) * dx) / dy;
227  x2 = (x < xMin) ? xMin : (x > xMax) ? xMax : x;
228  y2 = yMax;
229  }
230  if (y1 > y2) {
231  y1 = y2;
232  }
233  if (x1 < x2) {
234  x2 = x1;
235  }
236  cov += (xMax - x1 + 0.5 * (x2 - x1)) * (y2 - y1);
237  }
238  return cov;
239 }
240 
243 class SweepLine {
244 public:
245  SweepLine(lsst::afw::image::Image<float>::Ptr img, double startY) :
246  _cov(new double[img->getWidth()]),
247  _img(img),
248  _y(std::max(image::indexToPosition(0) - 0.5, startY)),
250  _head(0)
251  {
252  for (int i = 0; i < _img->getWidth(); ++i) {
253  _cov[i] = 0.0;
254  }
255  }
256 
259  void add(Edge &e) {
260  if (_yIndex >= _img->getHeight()) {
261  return;
262  }
263  // Insertion sort - in the typical case just 2 edges will
264  // be in the sweep line for a given y. More complex data
265  // structures are therefore unwarranted.
266  Edge *after = 0;
267  Edge *before = _head;
268  while (before != 0 && e.rightOf(*before)) {
269  after = before;
270  before = before->next;
271  }
272  if (after != 0) {
273  after->next = &e;
274  } else {
275  _head = &e;
276  }
277  e.next = before;
278  }
279 
280  void advance(double y);
281  void finish();
282 
283 private:
287  void removeBelow(double y) {
288  Edge *prev = 0;
289  Edge *e = _head;
290  while (e != 0) {
291  if (e->v2->getY() <= y) {
292  // unlink e
293  if (prev == 0) {
294  _head = e->next;
295  } else {
296  prev->next = e->next;
297  }
298  } else {
299  prev = e;
300  }
301  e = e->next;
302  }
303  }
304 
308  void updateCoverage(int y) {
309  if (y < 0 || y >= _img->getHeight()) {
310  return;
311  }
312  typedef image::Image<float>::x_iterator XIter;
313  double *cov = _cov.get();
314  for (XIter i = _img->row_begin(y), e = _img->row_end(y);
315  i != e; ++i, ++cov) {
316  double c = *cov;
317  *cov = 0.0; // clear coverage information for next row
318  if (c > 0.0) {
319  *i += std::min(1.0f, static_cast<float>(c));
320  }
321  }
322  }
323 
324  void rasterizeCoverage(double yMin, double yMax);
325 
326  boost::scoped_array<double> _cov;
328  double _y;
329  int _yIndex;
330  // linked edge list
331  Edge *_head;
332 };
333 
334 void SweepLine::rasterizeCoverage(double yMin, double yMax) {
335  if (yMin >= yMax) {
336  return;
337  }
338  // rasterize left/right edge pairs
339  int const width = _img->getWidth();
340  Edge *left = _head;
341  while (left != 0) {
342  Edge *right = left->next;
343  if (right == 0) {
344  break;
345  }
346  std::pair<int, int> xl = left->getXBounds(yMin, yMax, width);
347  std::pair<int, int> xr = right->getXBounds(yMin, yMax, width);
348  for (int x = xl.first; x < xl.second; ++x) {
349  _cov[x] += left->getCoverage(yMin, yMax, x);
350  }
351  for (int x = xl.second; x < xr.second; ++x) {
352  _cov[x] += yMax - yMin;
353  }
354  for (int x = xr.first; x < xr.second; ++x) {
355  _cov[x] -= right->getCoverage(yMin, yMax, x);
356  }
357  left = right->next;
358  }
359 }
360 
361 void SweepLine::advance(double y) {
362  if (_yIndex >= _img->getHeight() || _head == 0 || y <= _y) {
363  return;
364  }
365  int const yIndex = image::positionToIndex(y);
366  int const height = _img->getHeight();
367  for (; _yIndex < yIndex && _yIndex < height; ++_yIndex) {
368  double yMax = image::indexToPosition(_yIndex) + 0.5;
369  rasterizeCoverage(_y, yMax);
370  updateCoverage(_yIndex);
371  _y = yMax;
372  }
373  if (yIndex < height) {
374  rasterizeCoverage(_y, y);
375  removeBelow(y);
376  _y = y;
377  } else {
378  _head = 0;
379  }
380 }
381 
382 void SweepLine::finish() {
383  double yMax = image::indexToPosition(_img->getHeight() - 1) + 0.5;
384  while (_head != 0) {
385  Edge *e = _head;
386  double y = yMax;
387  do {
388  y = std::min(y, e->v2->getY());
389  e = e->next;
390  } while (e != 0);
391  advance(y);
392  }
393  // flush coverage data to map and discard all edges
394  updateCoverage(_yIndex);
395  _head = 0;
396 }
397 
398 } // namespace
399 
400 
414  lsst::afw::table::SourceCatalog const & sources,
416  bool ignoreOffImage)
417 {
418  typedef table::SourceCatalog::const_iterator SourceIter;
419  for (SourceIter i = sources.begin(), e = sources.end(); i != e; ++i) {
420  Point2D xy = wcs->skyToPixel(i->getCoord());
421  int x = histogram->positionToIndex(xy[0], image::X).first;
422  int y = histogram->positionToIndex(xy[1], image::Y).first;
423  if (x < 0 || x >= histogram->getWidth() ||
424  y < 0 || y >= histogram->getHeight()) {
425  if (!ignoreOffImage) {
426  throw LSST_EXCEPT(except::RuntimeError,
427  "SourceCatalog contains sources lying "
428  "outside the histogram image");
429  }
430  continue;
431  }
432  histogram->operator()(x, y) += 1;
433  }
434 }
435 
444  std::vector<Point2D> const &verts,
446 {
447  typedef std::vector<Point2D>::const_iterator VertexIter;
448  typedef std::vector<Edge>::iterator EdgeIter;
449 
450  if (img->getWidth() <= 0 || img->getHeight() <= 0) {
451  throw LSST_EXCEPT(except::InvalidParameterError,
452  "image width/height must be at least 1");
453  }
454  if (verts.size() < 3) {
455  throw LSST_EXCEPT(except::InvalidParameterError,
456  "polygon must have at least 3 vertices");
457  }
458  std::vector<Edge> edges;
459  edges.reserve(verts.size());
460  double minY = image::indexToPosition(0) - 0.5;
461  double maxY = image::indexToPosition(img->getHeight()) - 0.5;
462  for (VertexIter v2 = verts.begin(), e = verts.end(), v1 = e - 1;
463  v2 != e; ++v2) {
464  VertexIter v = v1;
465  v1 = v2;
466  if (v->getY() == v2->getY()) {
467  continue; // omit horizontal and degenerate edges
468  } else if (v->getY() < v2->getY()) {
469  if (v->getY() >= maxY || v2->getY() < minY) {
470  continue; // edge not in y-range
471  }
472  } else {
473  if (v2->getY() >= maxY || v->getY() < minY) {
474  continue; // edge not in y-range
475  }
476  }
477  edges.push_back(Edge(*v, *v2));
478  }
479  if (edges.size() > 0) {
480  std::sort(edges.begin(), edges.end());
481  SweepLine sweeper(img, edges.front().v1->getY());
482  EdgeIter i = edges.begin(), e = edges.end();
483  // add all edges intersecting minY to sweep line
484  for (; i != e && i->v1->getY() <= minY; ++i) {
485  sweeper.add(*i);
486  }
487  while (i != e) {
488  double y = i->v1->getY();
489  sweeper.advance(y);
490  sweeper.add(*i);
491  for (++i; i != e && i->v1->getY() == y; ++i) {
492  sweeper.add(*i);
493  }
494  }
495  sweeper.finish();
496  }
497 }
498 
517  lsst::afw::image::Wcs::Ptr covMapWcs,
519  int width,
520  int height,
521  int step)
522 {
523  typedef std::vector<Point2D>::iterator VertexIter;
524  if (width <= 0 || height <= 0) {
525  throw LSST_EXCEPT(except::InvalidParameterError,
526  "Width/height of input image must be positive");
527  }
528  std::vector<Point2D> v;
529  if (step <= 0) {
530  v.reserve(4);
531  v.push_back(Point2D(-0.5, -0.5));
532  v.push_back(Point2D(width - 0.5, -0.5));
533  v.push_back(Point2D(width - 0.5, height - 0.5));
534  v.push_back(Point2D(-0.5, height - 0.5));
535  } else {
536  v.reserve(2 * width + 2 * height + 2);
537  for (int i = 0; i < width; i += step) {
538  v.push_back(Point2D(i - 0.5, -0.5));
539  }
540  for (int i = 0; i < height; i += step) {
541  v.push_back(Point2D(width - 0.5, i - 0.5));
542  }
543  for (int i = width; i > 0; i -= step) {
544  v.push_back(Point2D(i - 0.5, height - 0.5));
545  }
546  for (int i = height; i > 0; i -= step) {
547  v.push_back(Point2D(-0.5, i - 0.5));
548  }
549  }
550  // Convert from image pixel space to coverage map pixel space.
551  for (VertexIter i = v.begin(), e = v.end(); i != e; ++i) {
552  *i = covMapWcs->skyToPixel(*wcs->pixelToSky(*i));
553  }
554  // rasterize polygon
555  rasterizePolygon(v, covMap);
556 }
557 
558 }}} // namespace lsst::ap::utils
559 
int y
Edge * next
Definition: ImageUtils.cc:91
image::Image< float >::Ptr _img
Definition: ImageUtils.cc:327
double dx
Definition: ImageUtils.cc:90
void rasterizePolygon(std::vector< Point2D > const &verts, lsst::afw::image::Image< float >::Ptr img)
Definition: ImageUtils.cc:443
double indexToPosition(double ind)
Convert image index to image position.
Definition: ImageUtils.h:54
x_iterator row_begin(int y) const
Definition: Image.h:319
int _yIndex
Definition: ImageUtils.cc:329
x_iterator row_end(int y) const
Return an x_iterator to the end of the y&#39;th row.
Definition: Image.h:324
int positionToIndex(double pos)
Convert image position to nearest integer index.
Definition: ImageUtils.h:69
void makeSourceHistogram(lsst::afw::image::Image< unsigned short >::Ptr histogram, lsst::afw::table::SourceCatalog const &sources, lsst::afw::image::Wcs::Ptr wcs, bool ignoreOffImage)
Definition: ImageUtils.cc:412
void updateCoverageMap(lsst::afw::image::Image< float >::Ptr covMap, lsst::afw::image::Wcs::Ptr covMapWcs, lsst::afw::image::Wcs::Ptr wcs, int width, int height, int step)
Definition: ImageUtils.cc:515
boost::shared_ptr< Wcs > Ptr
Definition: Wcs.h:113
double dy
Definition: ImageUtils.cc:90
boost::shared_ptr< Image< PixelT > > Ptr
Definition: Image.h:418
double min
Definition: attributes.cc:216
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
Image utility methods.
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:237
double max
Definition: attributes.cc:218
boost::scoped_array< double > _cov
Definition: ImageUtils.cc:326
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
Definition: fwd.h:55
double _y
Definition: ImageUtils.cc:328
Point2D const * v2
Definition: ImageUtils.cc:89
bool operator==(Ellipse< DataT > const &a, Ellipse< DataT > const &b)
Definition: EllipseTypes.h:100
int x
std::pair< int, double > positionToIndex(double const pos, lsst::afw::image::xOrY const xy) const
Convert image position to index (nearest integer and fractional parts)
Definition: Image.h:271
A coordinate class intended to represent absolute positions.
Point2D const * v1
Definition: ImageUtils.cc:88
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
const double PixelZeroPos
FITS uses 1.0, SDSS uses 0.5, LSST uses 0.0 (http://dev.lsstcorp.org/trac/wiki/BottomLeftPixelProposa...
Definition: ImageUtils.h:42
afw::table::Key< double > b
Point< double, 2 > Point2D
Definition: Point.h:286
int getHeight() const
Return the number of rows in the image.
Definition: Image.h:239
Base::const_iterator const_iterator
Definition: SortedCatalog.h:49
Edge * _head
Definition: ImageUtils.cc:331
bool operator<(Ellipse< DataT > const &a, Ellipse< DataT > const &b)
Definition: EllipseTypes.h:95
A class to represent a 2-dimensional array of pixels.
Definition: PSF.h:43
Include files required for standard LSST Exception handling.