LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
warpExposure.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*- // fixed format comment for emacs
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 
33 #include <cassert>
34 #include <cmath>
35 #include <limits>
36 #include <sstream>
37 #include <string>
38 #include <vector>
39 #include <utility>
40 #include <ctime>
41 
42 #include "boost/shared_ptr.hpp"
43 #include "boost/pointer_cast.hpp"
44 #include "boost/cstdint.hpp"
45 #include "boost/regex.hpp"
46 
47 #include "lsst/pex/logging/Trace.h"
48 #include "lsst/pex/exceptions.h"
50 #include "lsst/afw/geom.h"
51 #include "lsst/afw/math/Kernel.h"
52 #include "lsst/afw/coord/Coord.h"
53 #include "lsst/afw/image/Calib.h"
54 #include "lsst/afw/image/Wcs.h"
61 
62 namespace pexExcept = lsst::pex::exceptions;
63 namespace pexLog = lsst::pex::logging;
64 namespace afwImage = lsst::afw::image;
65 namespace afwGeom = lsst::afw::geom;
66 namespace afwCoord = lsst::afw::coord;
67 namespace afwMath = lsst::afw::math;
68 
69 
70 //
71 // A helper function for the warping kernels which provides error-checking:
72 // the warping kernels are designed to work in two cases
73 // 0 < x < 1 and ctrX=(size-1)/2
74 // -1 < x < 0 and ctrX=(size+1)/2
75 // (and analogously for y). Note that to get the second case, Kernel::setCtrX(1) must be
76 // called before calling Kernel::setKernelParameter(). [see afw::math::offsetImage() for
77 // an example]
78 //
79 // FIXME eventually the 3 warping kernels will inherit from a common base class WarpingKernel
80 // and this routine can be eliminated by putting the code in WarpingKernel::setKernelParameter()
81 //
82 static inline void checkWarpingKernelParameter(const afwMath::SeparableKernel *p, unsigned int ind, double value)
83 {
84  if (ind > 1) {
85  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad ind argument in WarpingKernel::setKernelParameter()");
86  }
87  int ctr = p->getCtr()[ind];
88  int size = p->getDimensions()[ind];
89 
90  if (ctr == (size-1)/2) {
91  if (value < -1e-6 || value > 1+1e-6) {
92  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad coordinate in WarpingKernel::setKernelParameter()");
93  }
94  } else if (ctr == (size+1)/2) {
95  if (value < -1-1e-6 || value > 1e-6) {
96  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad coordinate in WarpingKernel::setKernelParameter()");
97  }
98  } else {
99  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad ctr value in WarpingKernel::setKernelParameter()");
100  }
101 }
102 
103 
104 PTR(afwMath::Kernel) afwMath::LanczosWarpingKernel::clone() const {
105  return PTR(afwMath::Kernel)(new afwMath::LanczosWarpingKernel(this->getOrder()));
106 }
107 
112  return this->getWidth() / 2;
113 }
114 
115 void afwMath::LanczosWarpingKernel::setKernelParameter(unsigned int ind, double value) const
116 {
117  checkWarpingKernelParameter(this, ind, value);
119 }
120 
121 PTR(afwMath::Kernel) afwMath::BilinearWarpingKernel::clone() const {
123 }
124 
132 afwMath::Kernel::Pixel afwMath::BilinearWarpingKernel::BilinearFunction1::operator() (double x) const
133 {
134  //
135  // this->_params[0] = value of x where we want to interpolate the function
136  // x = integer value of x where we evaluate the function in the interpolation
137  //
138  // The following weird-looking expression has no if/else statements, is roundoff-tolerant,
139  // and works in the following two cases:
140  // 0 < this->_params[0] < 1, x \in {0,1}
141  // -1 < this->_params[0] < 0, x \in {-1,0}
142  //
143  // The checks in BilinearWarpingKernel::setKernelParameter() ensure that one of these
144  // conditions is satisfied
145  //
146  return 0.5 + (1.0 - (2.0 * fabs(this->_params[0]))) * (0.5 - fabs(x));
147 }
148 
149 void afwMath::BilinearWarpingKernel::setKernelParameter(unsigned int ind, double value) const
150 {
151  checkWarpingKernelParameter(this, ind, value);
153 }
154 
158 std::string afwMath::BilinearWarpingKernel::BilinearFunction1::toString(std::string const& prefix) const {
159  std::ostringstream os;
160  os << "_BilinearFunction1: ";
161  os << Function1<Kernel::Pixel>::toString(prefix);
162  return os.str();
163 }
164 
165 PTR(afwMath::Kernel) afwMath::NearestWarpingKernel::clone() const {
167 }
168 
176 afwMath::Kernel::Pixel afwMath::NearestWarpingKernel::NearestFunction1::operator() (double x) const {
177  // this expression is faster than using conditionals, but offers no sanity checking
178  return static_cast<double>((fabs(this->_params[0]) < 0.5) == (fabs(x) < 0.5));
179 }
180 
181 void afwMath::NearestWarpingKernel::setKernelParameter(unsigned int ind, double value) const
182 {
183  checkWarpingKernelParameter(this, ind, value);
185 }
186 
190 std::string afwMath::NearestWarpingKernel::NearestFunction1::toString(std::string const& prefix) const {
191  std::ostringstream os;
192  os << "_NearestFunction1: ";
193  os << Function1<Kernel::Pixel>::toString(prefix);
194  return os.str();
195 }
196 
197 boost::shared_ptr<afwMath::SeparableKernel> afwMath::makeWarpingKernel(std::string name) {
198  typedef boost::shared_ptr<afwMath::SeparableKernel> KernelPtr;
199  boost::cmatch matches;
200  static const boost::regex LanczosRE("lanczos(\\d+)");
201  if (name == "bilinear") {
202  return KernelPtr(new BilinearWarpingKernel());
203  } else if (boost::regex_match(name.c_str(), matches, LanczosRE)) {
204  std::string orderStr(matches[1].first, matches[1].second);
205  int order;
206  std::istringstream(orderStr) >> order;
207  return KernelPtr(new LanczosWarpingKernel(order));
208  } else if (name == "nearest") {
209  return KernelPtr(new NearestWarpingKernel());
210  } else {
211  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
212  "unknown warping kernel name: \"" + name + "\"");
213  }
214 }
215 
216 PTR(afwMath::SeparableKernel) afwMath::WarpingControl::getWarpingKernel() const {
217  if (_warpingKernelPtr->getCacheSize() != _cacheSize) {
218  _warpingKernelPtr->computeCache(_cacheSize);
219  }
220  return _warpingKernelPtr;
221 };
222 
224  std::string const &warpingKernelName
225 ) {
226  PTR(SeparableKernel) warpingKernelPtr(makeWarpingKernel(warpingKernelName));
227  setWarpingKernel(*warpingKernelPtr);
228 }
229 
231  SeparableKernel const &warpingKernel
232 ) {
233  if (_maskWarpingKernelPtr) {
234  _testWarpingKernels(warpingKernel, *_maskWarpingKernelPtr);
235  }
236  PTR(SeparableKernel) warpingKernelPtr(boost::static_pointer_cast<SeparableKernel>(warpingKernel.clone()));
237  _testDevicePreference(_devicePreference, warpingKernelPtr);
238  _warpingKernelPtr = warpingKernelPtr;
239 }
240 
241 
242 PTR(afwMath::SeparableKernel) afwMath::WarpingControl::getMaskWarpingKernel() const {
243  if (_maskWarpingKernelPtr) { // lazily update kernel cache
244  if (_maskWarpingKernelPtr->getCacheSize() != _cacheSize) {
245  _maskWarpingKernelPtr->computeCache(_cacheSize);
246  }
247  }
248  return _maskWarpingKernelPtr;
249 }
250 
252  std::string const &maskWarpingKernelName
253 ) {
254  if (!maskWarpingKernelName.empty()) {
255  PTR(SeparableKernel) maskWarpingKernelPtr(makeWarpingKernel(maskWarpingKernelName));
256  setMaskWarpingKernel(*maskWarpingKernelPtr);
257  } else {
258  _maskWarpingKernelPtr.reset();
259  }
260 }
261 
263  SeparableKernel const & maskWarpingKernel
264 ) {
265  _testWarpingKernels(*_warpingKernelPtr, maskWarpingKernel);
266  _maskWarpingKernelPtr = boost::static_pointer_cast<SeparableKernel>(maskWarpingKernel.clone());
267 }
268 
269 
271  SeparableKernel const &warpingKernel,
272  SeparableKernel const &maskWarpingKernel
273 ) const {
276  warpingKernel.getDimensions()
277  );
279  lsst::afw::geom::Point2I(0, 0) - lsst::afw::geom::Extent2I(maskWarpingKernel.getCtr()),
280  maskWarpingKernel.getDimensions()
281  );
282  if (!kernelBBox.contains(maskKernelBBox)) {
283  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
284  "warping kernel is smaller than mask warping kernel");
285  }
286 }
287 
289  lsst::afw::gpu::DevicePreference const &devicePreference,
290  CONST_PTR(SeparableKernel) const &warpingKernelPtr
291 ) const {
292  CONST_PTR(LanczosWarpingKernel) const lanczosKernelPtr =
293  boost::dynamic_pointer_cast<const LanczosWarpingKernel>(warpingKernelPtr);
294  if (devicePreference == lsst::afw::gpu::USE_GPU && !lanczosKernelPtr) {
295  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
296  "devicePreference = USE_GPU, but warping kernel not Lanczos");
297  }
298 }
299 
300 
301 
302 template<typename DestExposureT, typename SrcExposureT>
304  DestExposureT &destExposure,
305  SrcExposureT const &srcExposure,
306  afwMath::WarpingControl const &control,
307  typename DestExposureT::MaskedImageT::SinglePixel padValue
308  )
309 {
310  if (!destExposure.hasWcs()) {
311  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "destExposure has no Wcs");
312  }
313  if (!srcExposure.hasWcs()) {
314  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "srcExposure has no Wcs");
315  }
316  typename DestExposureT::MaskedImageT mi = destExposure.getMaskedImage();
317  boost::shared_ptr<afwImage::Calib> calibCopy(new afwImage::Calib(*srcExposure.getCalib()));
318  destExposure.setCalib(calibCopy);
319  destExposure.setFilter(srcExposure.getFilter());
320  return warpImage(mi, *destExposure.getWcs(), srcExposure.getMaskedImage(), *srcExposure.getWcs(),
321  control, padValue);
322 }
323 
324 template<typename DestExposureT, typename SrcExposureT>
326  DestExposureT &destExposure,
327  SrcExposureT const &srcExposure,
328  afwMath::SeparableKernel &warpingKernel,
329  int const interpLength,
330  typename DestExposureT::MaskedImageT::SinglePixel padValue,
332  )
333 {
334  if (!destExposure.hasWcs()) {
335  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "destExposure has no Wcs");
336  }
337  if (!srcExposure.hasWcs()) {
338  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "srcExposure has no Wcs");
339  }
340  typename DestExposureT::MaskedImageT mi = destExposure.getMaskedImage();
341  boost::shared_ptr<afwImage::Calib> calibCopy(new afwImage::Calib(*srcExposure.getCalib()));
342  destExposure.setCalib(calibCopy);
343  destExposure.setFilter(srcExposure.getFilter());
344  return warpImage(mi, *destExposure.getWcs(),
345  srcExposure.getMaskedImage(), *srcExposure.getWcs(), warpingKernel, interpLength,
346  padValue, devPref);
347 }
348 
349 
350 /************************************************************************************************************/
351 namespace {
352 
353  inline afwGeom::Point2D computeSrcPos(
354  int destCol,
355  int destRow,
356  afwGeom::Point2D const &destXY0,
357  afwImage::Wcs const &destWcs,
358  afwImage::Wcs const &srcWcs)
359  {
360  double const col = afwImage::indexToPosition(destCol + destXY0[0]);
361  double const row = afwImage::indexToPosition(destRow + destXY0[1]);
362  afwGeom::Angle sky1, sky2;
363  destWcs.pixelToSky(col, row, sky1, sky2);
364  return srcWcs.skyToPixel(sky1, sky2);
365  }
366 
367 
368  inline double computeRelativeArea(
369  afwGeom::Point2D const &srcPos,
370  afwGeom::Point2D const &leftSrcPos,
371  afwGeom::Point2D const &upSrcPos)
372  {
373  afwGeom::Extent2D dSrcA = srcPos - leftSrcPos;
374  afwGeom::Extent2D dSrcB = srcPos - upSrcPos;
375 
376  return std::abs(dSrcA.getX()*dSrcB.getY() - dSrcA.getY()*dSrcB.getX());
377  }
378 
379  template<typename DestImageT, typename SrcImageT>
380  int doWarpImage(
381  DestImageT &destImage,
382  SrcImageT const &srcImage,
383  afwMath::detail::PositionFunctor const &computeSrcPos,
384  afwMath::WarpingControl const &control,
385  typename DestImageT::SinglePixel padValue
386  ) {
387  if (afwMath::details::isSameObject(destImage, srcImage)) {
388  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
389  "destImage is srcImage; cannot warp in place");
390  }
391  if (destImage.getBBox(afwImage::LOCAL).isEmpty()) {
392  return 0;
393  }
394  // if src image is too small then don't try to warp
395  try {
396  PTR(afwMath::SeparableKernel) warpingKernelPtr = control.getWarpingKernel();
397  warpingKernelPtr->shrinkBBox(srcImage.getBBox(afwImage::LOCAL));
398  } catch(...) {
399  for (int y = 0, height = destImage.getHeight(); y < height; ++y) {
400  for (typename DestImageT::x_iterator destPtr = destImage.row_begin(y), end = destImage.row_end(y);
401  destPtr != end; ++destPtr) {
402  *destPtr = padValue;
403  }
404  }
405  return 0;
406  }
407  PTR(afwMath::SeparableKernel) warpingKernelPtr = control.getWarpingKernel();
408  int interpLength = control.getInterpLength();
409  lsst::afw::gpu::DevicePreference devPref = control.getDevicePreference();
410 
411  boost::shared_ptr<afwMath::LanczosWarpingKernel const> const lanczosKernelPtr =
412  boost::dynamic_pointer_cast<afwMath::LanczosWarpingKernel>(warpingKernelPtr);
413 
414  if (lsst::afw::gpu::isGpuEnabled()) {
415  if(!lanczosKernelPtr) {
416  if (devPref == lsst::afw::gpu::USE_GPU) {
417  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "Gpu can process only Lanczos kernels");
418  }
419  } else if (devPref == lsst::afw::gpu::USE_GPU || (lsst::afw::gpu::isGpuBuild() && interpLength > 0) ) {
420  PTR(afwMath::SeparableKernel) maskWarpingKernelPtr = control.getWarpingKernel();
421  if (control.getMaskWarpingKernel() )
422  maskWarpingKernelPtr = control.getMaskWarpingKernel();
423  if (devPref == lsst::afw::gpu::AUTO_WITH_CPU_FALLBACK) {
424  try {
425  std::pair<int, afwMath::detail::WarpImageGpuStatus::ReturnCode> result =
426  afwMath::detail::warpImageGPU(destImage, srcImage,
427  *lanczosKernelPtr, *maskWarpingKernelPtr,
428  computeSrcPos, interpLength, padValue, false);
429  if (result.second == afwMath::detail::WarpImageGpuStatus::OK) return result.first;
430  }
431  catch(lsst::afw::gpu::GpuMemoryError) { }
432  catch(pexExcept::MemoryError) { }
433  catch(lsst::afw::gpu::GpuRuntimeError) { }
434  } else if (devPref != lsst::afw::gpu::USE_CPU) {
435  std::pair<int, afwMath::detail::WarpImageGpuStatus::ReturnCode> result =
436  afwMath::detail::warpImageGPU(destImage, srcImage,
437  *lanczosKernelPtr, *maskWarpingKernelPtr,
438  computeSrcPos, interpLength, padValue,
439  devPref == lsst::afw::gpu::USE_GPU);
440  if (result.second == afwMath::detail::WarpImageGpuStatus::OK) return result.first;
441  if (devPref == lsst::afw::gpu::USE_GPU) {
442  throw LSST_EXCEPT(pexExcept::RuntimeError,
443  "Gpu cannot perform this warp (kernel too big?)");
444  }
445  }
446  }
447  }
448 
449  int numGoodPixels = 0;
450 
451  typedef afwImage::Image<afwMath::Kernel::Pixel> KernelImageT;
452 
453  // Get the source MaskedImage and a pixel accessor to it.
454  int const srcWidth = srcImage.getWidth();
455  int const srcHeight = srcImage.getHeight();
456  pexLog::TTrace<3>("lsst.afw.math.warp", "source image width=%d; height=%d", srcWidth, srcHeight);
457 
458  int const destWidth = destImage.getWidth();
459  int const destHeight = destImage.getHeight();
460 
461  pexLog::TTrace<3>("lsst.afw.math.warp", "remap image width=%d; height=%d", destWidth, destHeight);
462 
463  // Set each pixel of destExposure's MaskedImage
464  pexLog::TTrace<4>("lsst.afw.math.warp", "Remapping masked image");
465 
466  // A cache of pixel positions on the source corresponding to the previous or current row
467  // of the destination image.
468  // The first value is for column -1 because the previous source position is used to compute relative area
469  // To simplify the indexing, use an iterator that starts at begin+1, thus:
470  // srcPosView = _srcPosList.begin() + 1
471  // srcPosView[col-1] and lower indices are for this row
472  // srcPosView[col] and higher indices are for the previous row
473  std::vector<afwGeom::Point2D> _srcPosList(1 + destWidth);
474  std::vector<afwGeom::Point2D>::iterator const srcPosView = _srcPosList.begin() + 1;
475 
476  int const maxCol = destWidth - 1;
477  int const maxRow = destHeight - 1;
478 
479  afwMath::detail::WarpAtOnePoint<DestImageT, SrcImageT> warpAtOnePoint(srcImage, control, padValue);
480 
481  if (interpLength > 0) {
482  // Use interpolation. Note that 1 produces the same result as no interpolation
483  // but uses this code branch, thus providing an easy way to compare the two branches.
484 
485  // Estimate for number of horizontal interpolation band edges, to reserve memory in vectors
486  int const numColEdges = 2 + ((destWidth - 1) / interpLength);
487 
488  // A list of edge column indices for interpolation bands;
489  // starts at -1, increments by interpLen (except the final interval), and ends at destWidth-1
490  std::vector<int> edgeColList;
491  edgeColList.reserve(numColEdges);
492 
493  // A list of 1/column width for horizontal interpolation bands; the first value is garbage.
494  // The inverse is used for speed because the values is always multiplied.
495  std::vector<double> invWidthList;
496  invWidthList.reserve(numColEdges);
497 
498  // Compute edgeColList and invWidthList
499  edgeColList.push_back(-1);
500  invWidthList.push_back(0.0);
501  for (int prevEndCol = -1; prevEndCol < maxCol; prevEndCol += interpLength) {
502  int endCol = prevEndCol + interpLength;
503  if (endCol > maxCol) {
504  endCol = maxCol;
505  }
506  edgeColList.push_back(endCol);
507  assert(endCol - prevEndCol > 0);
508  invWidthList.push_back(1.0 / static_cast<double>(endCol - prevEndCol));
509  }
510  assert(edgeColList.back() == maxCol);
511 
512  // A list of delta source positions along the edge columns of the horizontal interpolation bands
513  std::vector<afwGeom::Extent2D> yDeltaSrcPosList(edgeColList.size());
514 
515  // Initialize _srcPosList for row -1
516  //srcPosView[-1] = computeSrcPos(-1, -1, destXY0, destWcs, srcWcs);
517  srcPosView[-1] = computeSrcPos(-1, -1);
518  for (int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
519  int const prevEndCol = edgeColList[colBand-1];
520  int const endCol = edgeColList[colBand];
521  afwGeom::Point2D leftSrcPos = srcPosView[prevEndCol];
522  afwGeom::Point2D rightSrcPos = computeSrcPos(endCol, -1);
523  afwGeom::Extent2D xDeltaSrcPos = (rightSrcPos - leftSrcPos) * invWidthList[colBand];
524 
525  for (int col = prevEndCol + 1; col <= endCol; ++col) {
526  srcPosView[col] = srcPosView[col-1] + xDeltaSrcPos;
527  }
528  }
529 
530  int endRow = -1;
531  while (endRow < maxRow) {
532  // Next horizontal interpolation band
533 
534  int prevEndRow = endRow;
535  endRow = prevEndRow + interpLength;
536  if (endRow > maxRow) {
537  endRow = maxRow;
538  }
539  assert(endRow - prevEndRow > 0);
540  double interpInvHeight = 1.0 / static_cast<double>(endRow - prevEndRow);
541 
542  // Set yDeltaSrcPosList for this horizontal interpolation band
543  for (int colBand = 0, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
544  int endCol = edgeColList[colBand];
545  afwGeom::Point2D bottomSrcPos = computeSrcPos(endCol, endRow);
546  yDeltaSrcPosList[colBand] = (bottomSrcPos - srcPosView[endCol]) * interpInvHeight;
547  }
548 
549  for (int row = prevEndRow + 1; row <= endRow; ++row) {
550  typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
551  srcPosView[-1] += yDeltaSrcPosList[0];
552  for (int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
554 
555  int const prevEndCol = edgeColList[colBand-1];
556  int const endCol = edgeColList[colBand];
557 
558  // Compute xDeltaSrcPos; remember that srcPosView contains
559  // positions for this row in prevEndCol and smaller indices,
560  // and positions for the previous row for larger indices (including endCol)
561  afwGeom::Point2D leftSrcPos = srcPosView[prevEndCol];
562  afwGeom::Point2D rightSrcPos = srcPosView[endCol] + yDeltaSrcPosList[colBand];
563  afwGeom::Extent2D xDeltaSrcPos = (rightSrcPos - leftSrcPos) * invWidthList[colBand];
564 
565  for (int col = prevEndCol + 1; col <= endCol; ++col, ++destXIter) {
566  afwGeom::Point2D leftSrcPos = srcPosView[col-1];
567  afwGeom::Point2D srcPos = leftSrcPos + xDeltaSrcPos;
568  double relativeArea = computeRelativeArea(srcPos, leftSrcPos, srcPosView[col]);
569 
570  srcPosView[col] = srcPos;
571 
572  if (warpAtOnePoint(destXIter, srcPos, relativeArea,
574  ++numGoodPixels;
575  }
576  } // for col
577  } // for col band
578  } // for row
579  } // while next row band
580 
581 
582  } else {
583  // No interpolation
584 
585  // initialize _srcPosList for row -1;
586  // the first value is not needed, but it's safer to compute it
587  std::vector<afwGeom::Point2D>::iterator srcPosView = _srcPosList.begin() + 1;
588  for (int col = -1; col < destWidth; ++col) {
589  srcPosView[col] = computeSrcPos(col, -1);
590  }
591 
592  for (int row = 0; row < destHeight; ++row) {
593  typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
594 
595  srcPosView[-1] = computeSrcPos(-1, row);
596 
597  for (int col = 0; col < destWidth; ++col, ++destXIter) {
598  afwGeom::Point2D srcPos = computeSrcPos(col, row);
599  double relativeArea = computeRelativeArea(srcPos, srcPosView[col-1], srcPosView[col]);
600  srcPosView[col] = srcPos;
601 
602  if (warpAtOnePoint(destXIter, srcPos, relativeArea,
604  ++numGoodPixels;
605  }
606  } // for col
607  } // for row
608  } // if interp
609 
610  return numGoodPixels;
611  }
612 
613 } // namespace
614 
615 template<typename DestImageT, typename SrcImageT>
617  DestImageT &destImage,
618  lsst::afw::image::Wcs const &destWcs,
619  SrcImageT const &srcImage,
620  lsst::afw::image::Wcs const &srcWcs,
621  afwMath::WarpingControl const &control,
622  typename DestImageT::SinglePixel padValue
623 ) {
624  afwGeom::Point2D const destXY0(destImage.getXY0());
625  afwMath::detail::WcsPositionFunctor const computeSrcPos(destXY0, destWcs, srcWcs);
626  return doWarpImage(destImage, srcImage, computeSrcPos, control, padValue);
627 }
628 
629 template<typename DestImageT, typename SrcImageT>
631  DestImageT &destImage,
632  afwImage::Wcs const &destWcs,
633  SrcImageT const &srcImage,
634  afwImage::Wcs const &srcWcs,
635  afwMath::SeparableKernel &warpingKernel,
636  int const interpLength,
637  typename DestImageT::SinglePixel padValue,
639 ) {
640  afwGeom::Point2D const destXY0(destImage.getXY0());
641  afwMath::detail::WcsPositionFunctor const computeSrcPos(destXY0, destWcs, srcWcs);
642  afwMath::WarpingControl control(warpingKernel, interpLength, devPref);
643  return doWarpImage(destImage, srcImage, computeSrcPos, control, padValue);
644 }
645 
646 
647 template<typename DestImageT, typename SrcImageT>
649  DestImageT &destImage,
650  SrcImageT const &srcImage,
651  afwGeom::AffineTransform const &affineTransform,
652  afwMath::WarpingControl const &control,
653  typename DestImageT::SinglePixel padValue
654 ) {
655  afwGeom::Point2D const destXY0(destImage.getXY0());
656  afwMath::detail::AffineTransformPositionFunctor const computeSrcPos(destXY0, affineTransform);
657  return doWarpImage(destImage, srcImage, computeSrcPos, control, padValue);
658 }
659 
660 
661 template<typename DestImageT, typename SrcImageT>
663  DestImageT &destImage,
664  SrcImageT const &srcImage,
665  afwMath::SeparableKernel &warpingKernel,
666  afwGeom::AffineTransform const &affineTransform,
667  int const interpLength,
668  typename DestImageT::SinglePixel padValue,
670  )
671 {
672  afwGeom::Point2D const destXY0(destImage.getXY0());
673  afwMath::detail::AffineTransformPositionFunctor const computeSrcPos(destXY0, affineTransform);
674  afwMath::WarpingControl control(warpingKernel, interpLength, devPref);
675  return doWarpImage(destImage, srcImage, computeSrcPos, control, padValue);
676 }
677 
678 
679 template<typename DestImageT, typename SrcImageT>
681  DestImageT &destImage,
682  SrcImageT const &srcImage,
683  afwGeom::LinearTransform const &linearTransform,
684  afwGeom::Point2D const &centerPosition,
685  afwMath::WarpingControl const &control,
686  typename DestImageT::SinglePixel padValue
687 ) {
688  // force src and dest to be the same size and xy0
689  if (
690  (destImage.getWidth() != srcImage.getWidth()) ||
691  (destImage.getHeight() != srcImage.getHeight()) ||
692  (destImage.getXY0() != srcImage.getXY0())
693  ) {
694  std::ostringstream errStream;
695  errStream << "src and dest images must have same size and xy0.";
696  throw LSST_EXCEPT(pexExcept::InvalidParameterError, errStream.str());
697  }
698 
699  // set the xy0 coords to 0,0 to make life easier
700  SrcImageT srcImageCopy(srcImage, true);
701  srcImageCopy.setXY0(0, 0);
702  destImage.setXY0(0, 0);
703  afwGeom::Extent2D cLocal = afwGeom::Extent2D(centerPosition) - afwGeom::Extent2D(srcImage.getXY0());
704 
705  // for the affine transform, the centerPosition will not only get sheared, but also
706  // moved slightly. So we'll include a translation to move it back by an amount
707  // centerPosition - translatedCenterPosition
708  afwGeom::AffineTransform affTran(linearTransform, cLocal - linearTransform(cLocal));
709 
710  // now warp
711 #if 0
712  static float t = 0.0;
713  float t_before = 1.0*clock()/CLOCKS_PER_SEC;
714  int n = warpImage(destImage, srcImageCopy, affTran, control, padValue);
715  float t_after = 1.0*clock()/CLOCKS_PER_SEC;
716  float dt = t_after - t_before;
717  t += dt;
718  std::cout <<srcImage.getWidth()<<"x"<<srcImage.getHeight()<<": "<< dt <<" "<< t <<std::endl;
719 #else
720  int n = warpImage(destImage, srcImageCopy, affTran, control, padValue);
721 #endif
722 
723  // fix the origin and we're done.
724  destImage.setXY0(srcImage.getXY0());
725 
726  return n;
727 }
728 
729 
730 template<typename DestImageT, typename SrcImageT>
732  DestImageT &destImage,
733  SrcImageT const &srcImage,
734  afwMath::SeparableKernel &warpingKernel,
735  afwGeom::LinearTransform const &linearTransform,
736  afwGeom::Point2D const &centerPosition,
737  int const interpLength,
738  typename DestImageT::SinglePixel padValue,
740 ) {
741  afwMath::WarpingControl control(warpingKernel, interpLength, devPref);
742  return warpCenteredImage(destImage, srcImage, linearTransform, centerPosition, control, padValue);
743 }
744 
745 
746 //
747 // Explicit instantiations
748 //
750 // may need to omit default params for EXPOSURE -- original code did that and it worked
751 #define EXPOSURE(PIXTYPE) afwImage::Exposure<PIXTYPE, afwImage::MaskPixel, afwImage::VariancePixel>
752 #define MASKEDIMAGE(PIXTYPE) afwImage::MaskedImage<PIXTYPE, afwImage::MaskPixel, afwImage::VariancePixel>
753 #define IMAGE(PIXTYPE) afwImage::Image<PIXTYPE>
754 #define NL /* */
755 
756 #define INSTANTIATE(DESTIMAGEPIXELT, SRCIMAGEPIXELT) \
757  template int afwMath::warpCenteredImage( \
758  IMAGE(DESTIMAGEPIXELT) &destImage, \
759  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
760  afwGeom::LinearTransform const &linearTransform, \
761  afwGeom::Point2D const &centerPosition, \
762  afwMath::WarpingControl const &control, \
763  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
764  template int afwMath::warpCenteredImage( \
765  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
766  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
767  afwGeom::LinearTransform const &linearTransform, \
768  afwGeom::Point2D const &centerPosition, \
769  afwMath::WarpingControl const &control, \
770  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
771  template int afwMath::warpCenteredImage( \
772  IMAGE(DESTIMAGEPIXELT) &destImage, \
773  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
774  afwMath::SeparableKernel &warpingKernel, \
775  afwGeom::LinearTransform const &linearTransform, \
776  afwGeom::Point2D const &centerPosition, \
777  int const interpLength, \
778  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue, \
779  lsst::afw::gpu::DevicePreference devPref); NL \
780  template int afwMath::warpCenteredImage( \
781  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
782  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
783  afwMath::SeparableKernel &warpingKernel, \
784  afwGeom::LinearTransform const &linearTransform, \
785  afwGeom::Point2D const &centerPosition, \
786  int const interpLength, \
787  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue, \
788  lsst::afw::gpu::DevicePreference devPref); NL \
789  template int afwMath::warpImage( \
790  IMAGE(DESTIMAGEPIXELT) &destImage, \
791  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
792  afwGeom::AffineTransform const &affineTransform, \
793  afwMath::WarpingControl const &control, \
794  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
795  template int afwMath::warpImage( \
796  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
797  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
798  afwGeom::AffineTransform const &affineTransform, \
799  afwMath::WarpingControl const &control, \
800  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
801  template int afwMath::warpImage( \
802  IMAGE(DESTIMAGEPIXELT) &destImage, \
803  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
804  afwMath::SeparableKernel &warpingKernel, \
805  afwGeom::AffineTransform const &affineTransform, \
806  int const interpLength, \
807  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue, \
808  lsst::afw::gpu::DevicePreference devPref); NL \
809  template int afwMath::warpImage( \
810  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
811  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
812  afwMath::SeparableKernel &warpingKernel, \
813  afwGeom::AffineTransform const &affineTransform, \
814  int const interpLength, \
815  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue, \
816  lsst::afw::gpu::DevicePreference devPref); NL \
817  template int afwMath::warpImage( \
818  IMAGE(DESTIMAGEPIXELT) &destImage, \
819  afwImage::Wcs const &destWcs, \
820  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
821  afwImage::Wcs const &srcWcs, \
822  afwMath::WarpingControl const &control, \
823  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
824  template int afwMath::warpImage( \
825  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
826  afwImage::Wcs const &destWcs, \
827  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
828  afwImage::Wcs const &srcWcs, \
829  afwMath::WarpingControl const &control, \
830  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
831  template int afwMath::warpImage( \
832  IMAGE(DESTIMAGEPIXELT) &destImage, \
833  afwImage::Wcs const &destWcs, \
834  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
835  afwImage::Wcs const &srcWcs, \
836  afwMath::SeparableKernel &warpingKernel, \
837  int const interpLength, \
838  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue, \
839  lsst::afw::gpu::DevicePreference devPref); NL \
840  template int afwMath::warpImage( \
841  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
842  afwImage::Wcs const &destWcs, \
843  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
844  afwImage::Wcs const &srcWcs, \
845  afwMath::SeparableKernel &warpingKernel, \
846  int const interpLength, \
847  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue, \
848  lsst::afw::gpu::DevicePreference devPref); NL \
849  template int afwMath::warpExposure( \
850  EXPOSURE(DESTIMAGEPIXELT) &destExposure, \
851  EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, \
852  afwMath::WarpingControl const &control,\
853  EXPOSURE(DESTIMAGEPIXELT)::MaskedImageT::SinglePixel padValue); NL \
854  template int afwMath::warpExposure( \
855  EXPOSURE(DESTIMAGEPIXELT) &destExposure, \
856  EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, \
857  afwMath::SeparableKernel &warpingKernel, \
858  int const interpLength, \
859  EXPOSURE(DESTIMAGEPIXELT)::MaskedImageT::SinglePixel padValue, \
860  lsst::afw::gpu::DevicePreference devPref);
861 
862 
863 
864 
865 INSTANTIATE(double, double)
866 INSTANTIATE(double, float)
867 INSTANTIATE(double, int)
868 INSTANTIATE(double, boost::uint16_t)
869 INSTANTIATE(float, float)
870 INSTANTIATE(float, int)
871 INSTANTIATE(float, boost::uint16_t)
872 INSTANTIATE(int, int)
873 INSTANTIATE(boost::uint16_t, boost::uint16_t)
bool isGpuEnabled()
returns true if GPU acceleration is enabled
virtual void setKernelParameter(unsigned int ind, double value) const
Set one kernel parameter.
GPU accelerared image warping.
An include file to include the header files for lsst::afw::geom.
virtual void setKernelParameter(unsigned int ind, double value) const
Set one kernel parameter.
Declare the Kernel class and subclasses.
geom::Extent2I const getDimensions() const
Return the Kernel&#39;s dimensions (width, height)
Definition: Kernel.h:226
#define PTR(...)
Definition: base.h:41
double indexToPosition(double ind)
Convert image index to image position.
Definition: ImageUtils.h:54
bool contains(Point2I const &point) const
Return true if the box contains the point.
int y
additional GPU exceptions
boost::shared_ptr< SeparableKernel > makeWarpingKernel(std::string name)
Return a warping kernel given its name.
geom::Point2D skyToPixel(geom::Angle sky1, geom::Angle sky2) const
Convert from sky coordinates (e.g ra/dec) to pixel positions.
Definition: Wcs.cc:808
definition of the Trace messaging facilities
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
Definition: Kernel.h:986
void setWarpingKernelName(std::string const &warpingKernelName)
set the warping kernel by name
void _testDevicePreference(lsst::afw::gpu::DevicePreference const &devicePreference, boost::shared_ptr< SeparableKernel const > const &warpingKernelPtr) const
test if GPU device preference and main warping kernel are compatible
Parameters to control convolution.
Definition: warpExposure.h:239
Implementation of the WCS standard for a any projection.
Definition: Wcs.h:107
int warpCenteredImage(DestImageT &destImage, SrcImageT const &srcImage, lsst::afw::geom::LinearTransform const &linearTransform, lsst::afw::geom::Point2D const &centerPosition, WarpingControl const &control, typename DestImageT::SinglePixel padValue=lsst::afw::math::edgePixel< DestImageT >(typename lsst::afw::image::detail::image_traits< DestImageT >::image_category()))
Warp an image with a LinearTranform about a specified point. This enables warping an image of e...
boost::shared_ptr< coord::Coord > pixelToSky(double pix1, double pix2) const
Convert from celestial coordinates to pixel coordinates.
Definition: Wcs.cc:894
DevicePreference
A type used to select whether to use CPU or GPU device.
void setMaskWarpingKernel(SeparableKernel const &maskWarpingKernel)
set the mask warping kernel
An integer coordinate rectangle.
Definition: Box.h:53
int warpImage(DestImageT &destImage, lsst::afw::image::Wcs const &destWcs, SrcImageT const &srcImage, lsst::afw::image::Wcs const &srcWcs, WarpingControl const &control, typename DestImageT::SinglePixel padValue=lsst::afw::math::edgePixel< DestImageT >(typename lsst::afw::image::detail::image_traits< DestImageT >::image_category()))
Warp an Image or MaskedImage to a new Wcs. See also convenience function warpExposure() to warp an Ex...
table::Key< table::Array< Kernel::Pixel > > image
Definition: FixedKernel.cc:117
int getOrder() const
get the order of the kernel
int getWidth() const
Return the Kernel&#39;s width.
Definition: Kernel.h:240
A 2D linear coordinate transformation.
int warpExposure(DestExposureT &destExposure, SrcExposureT const &srcExposure, WarpingControl const &control, typename DestExposureT::MaskedImageT::SinglePixel padValue=lsst::afw::math::edgePixel< typename DestExposureT::MaskedImageT >(typename lsst::afw::image::detail::image_traits< typename DestExposureT::MaskedImageT >::image_category()))
Warp (remap) one exposure to another.
Include files required for standard LSST Exception handling.
int x
An affine coordinate transformation consisting of a linear transformation and an offset.
void setMaskWarpingKernelName(std::string const &maskWarpingKernelName)
set or clear the mask warping kernel by name
bool isSameObject(A const &, B const &)
Definition: warpExposure.h:634
bool isGpuBuild()
Inline function which returns true only when GPU_BUILD macro is defined.
Definition: IsGpuBuild.h:45
void setWarpingKernel(SeparableKernel const &warpingKernel)
set the warping kernel
Interface for CPU/GPU device selection.
std::pair< int, WarpImageGpuStatus::ReturnCode > warpImageGPU(DestImageT &destImage, SrcImageT const &srcImage, lsst::afw::math::LanczosWarpingKernel const &warpingKernel, lsst::afw::math::SeparableKernel const &maskWarpingKernel, PositionFunctor const &computeSrcPos, int const interpLength, typename DestImageT::SinglePixel padValue, const bool forceProcessing=true)
GPU accelerated image warping using Lanczos resampling.
#define CONST_PTR(...)
Definition: base.h:47
lsst::afw::geom::Point2I getCtr() const
Return index of kernel&#39;s center.
Definition: Kernel.h:254
Bilinear warping: fast; good for undersampled data.
Definition: warpExposure.h:101
virtual void setKernelParameter(unsigned int ind, double value) const
Set one kernel parameter.
int row
Definition: CR.cc:153
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
tbl::Key< std::string > warpingKernelName
Definition: CoaddPsf.cc:328
Support for warping an image to a new WCS.
#define INSTANTIATE(T)
virtual void setKernelParameter(unsigned int ind, double value) const
Set one kernel parameter.
Nearest neighbor warping: fast; good for undersampled data.
Definition: warpExposure.h:157
void _testWarpingKernels(SeparableKernel const &warpingKernel, SeparableKernel const &maskWarpingKernel) const
Throw an exception if the two kernels are not compatible in shape.
Lanczos warping: accurate but slow and can introduce ringing artifacts.
Definition: warpExposure.h:72
ImageT::image_category image_category
Definition: Image.h:79
Extent< double, 2 > Extent2D
Definition: Extent.h:353
GPU accelerared image warping.
virtual boost::shared_ptr< Kernel > clone() const
Return a pointer to a deep copy of this kernel.
int getWidth() const
Return the number of columns in the image.
Definition: Image.h:237
Kernels are used for convolution with MaskedImages and (eventually) Images.
Definition: Kernel.h:134
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:415
int col
Definition: CR.cc:152
A function to determine whether compiling for GPU is enabled.
Functions to handle coordinates.