LSSTApplications  11.0-13-gbb96280,12.1.rc1,12.1.rc1+1,12.1.rc1+2,12.1.rc1+5,12.1.rc1+8,12.1.rc1-1-g06d7636+1,12.1.rc1-1-g253890b+5,12.1.rc1-1-g3d31b68+7,12.1.rc1-1-g3db6b75+1,12.1.rc1-1-g5c1385a+3,12.1.rc1-1-g83b2247,12.1.rc1-1-g90cb4cf+6,12.1.rc1-1-g91da24b+3,12.1.rc1-2-g3521f8a,12.1.rc1-2-g39433dd+4,12.1.rc1-2-g486411b+2,12.1.rc1-2-g4c2be76,12.1.rc1-2-gc9c0491,12.1.rc1-2-gda2cd4f+6,12.1.rc1-3-g3391c73+2,12.1.rc1-3-g8c1bd6c+1,12.1.rc1-3-gcf4b6cb+2,12.1.rc1-4-g057223e+1,12.1.rc1-4-g19ed13b+2,12.1.rc1-4-g30492a7
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 <cstdint>
36 #include <limits>
37 #include <sstream>
38 #include <string>
39 #include <vector>
40 #include <utility>
41 #include <ctime>
42 
43 #include <memory>
44 #include "boost/pointer_cast.hpp"
45 #include "boost/regex.hpp"
46 
47 #include "lsst/log/Log.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 afwImage = lsst::afw::image;
64 namespace afwGeom = lsst::afw::geom;
65 namespace afwCoord = lsst::afw::coord;
66 namespace afwMath = lsst::afw::math;
67 
68 
69 //
70 // A helper function for the warping kernels which provides error-checking:
71 // the warping kernels are designed to work in two cases
72 // 0 < x < 1 and ctrX=(size-1)/2
73 // -1 < x < 0 and ctrX=(size+1)/2
74 // (and analogously for y). Note that to get the second case, Kernel::setCtrX(1) must be
75 // called before calling Kernel::setKernelParameter(). [see afw::math::offsetImage() for
76 // an example]
77 //
78 // FIXME eventually the 3 warping kernels will inherit from a common base class WarpingKernel
79 // and this routine can be eliminated by putting the code in WarpingKernel::setKernelParameter()
80 //
81 static inline void checkWarpingKernelParameter(const afwMath::SeparableKernel *p, unsigned int ind, double value)
82 {
83  if (ind > 1) {
84  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad ind argument in WarpingKernel::setKernelParameter()");
85  }
86  int ctr = p->getCtr()[ind];
87  int size = p->getDimensions()[ind];
88 
89  if (ctr == (size-1)/2) {
90  if (value < -1e-6 || value > 1+1e-6) {
91  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad coordinate in WarpingKernel::setKernelParameter()");
92  }
93  } else if (ctr == (size+1)/2) {
94  if (value < -1-1e-6 || value > 1e-6) {
95  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad coordinate in WarpingKernel::setKernelParameter()");
96  }
97  } else {
98  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "bad ctr value in WarpingKernel::setKernelParameter()");
99  }
100 }
101 
102 
103 PTR(afwMath::Kernel) afwMath::LanczosWarpingKernel::clone() const {
104  return PTR(afwMath::Kernel)(new afwMath::LanczosWarpingKernel(this->getOrder()));
105 }
106 
111  return this->getWidth() / 2;
112 }
113 
114 void afwMath::LanczosWarpingKernel::setKernelParameter(unsigned int ind, double value) const
115 {
116  checkWarpingKernelParameter(this, ind, value);
118 }
119 
120 PTR(afwMath::Kernel) afwMath::BilinearWarpingKernel::clone() const {
122 }
123 
131 afwMath::Kernel::Pixel afwMath::BilinearWarpingKernel::BilinearFunction1::operator() (double x) const
132 {
133  //
134  // this->_params[0] = value of x where we want to interpolate the function
135  // x = integer value of x where we evaluate the function in the interpolation
136  //
137  // The following weird-looking expression has no if/else statements, is roundoff-tolerant,
138  // and works in the following two cases:
139  // 0 < this->_params[0] < 1, x \in {0,1}
140  // -1 < this->_params[0] < 0, x \in {-1,0}
141  //
142  // The checks in BilinearWarpingKernel::setKernelParameter() ensure that one of these
143  // conditions is satisfied
144  //
145  return 0.5 + (1.0 - (2.0 * fabs(this->_params[0]))) * (0.5 - fabs(x));
146 }
147 
148 void afwMath::BilinearWarpingKernel::setKernelParameter(unsigned int ind, double value) const
149 {
150  checkWarpingKernelParameter(this, ind, value);
152 }
153 
157 std::string afwMath::BilinearWarpingKernel::BilinearFunction1::toString(std::string const& prefix) const {
158  std::ostringstream os;
159  os << "_BilinearFunction1: ";
160  os << Function1<Kernel::Pixel>::toString(prefix);
161  return os.str();
162 }
163 
164 PTR(afwMath::Kernel) afwMath::NearestWarpingKernel::clone() const {
166 }
167 
175 afwMath::Kernel::Pixel afwMath::NearestWarpingKernel::NearestFunction1::operator() (double x) const {
176  // this expression is faster than using conditionals, but offers no sanity checking
177  return static_cast<double>((fabs(this->_params[0]) < 0.5) == (fabs(x) < 0.5));
178 }
179 
180 void afwMath::NearestWarpingKernel::setKernelParameter(unsigned int ind, double value) const
181 {
182  checkWarpingKernelParameter(this, ind, value);
184 }
185 
189 std::string afwMath::NearestWarpingKernel::NearestFunction1::toString(std::string const& prefix) const {
190  std::ostringstream os;
191  os << "_NearestFunction1: ";
192  os << Function1<Kernel::Pixel>::toString(prefix);
193  return os.str();
194 }
195 
196 std::shared_ptr<afwMath::SeparableKernel> afwMath::makeWarpingKernel(std::string name) {
197  typedef std::shared_ptr<afwMath::SeparableKernel> KernelPtr;
198  boost::cmatch matches;
199  static const boost::regex LanczosRE("lanczos(\\d+)");
200  if (name == "bilinear") {
201  return KernelPtr(new BilinearWarpingKernel());
202  } else if (boost::regex_match(name.c_str(), matches, LanczosRE)) {
203  std::string orderStr(matches[1].first, matches[1].second);
204  int order;
205  std::istringstream(orderStr) >> order;
206  return KernelPtr(new LanczosWarpingKernel(order));
207  } else if (name == "nearest") {
208  return KernelPtr(new NearestWarpingKernel());
209  } else {
210  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
211  "unknown warping kernel name: \"" + name + "\"");
212  }
213 }
214 
215 PTR(afwMath::SeparableKernel) afwMath::WarpingControl::getWarpingKernel() const {
216  if (_warpingKernelPtr->getCacheSize() != _cacheSize) {
217  _warpingKernelPtr->computeCache(_cacheSize);
218  }
219  return _warpingKernelPtr;
220 };
221 
223  std::string const &warpingKernelName
224 ) {
225  PTR(SeparableKernel) warpingKernelPtr(makeWarpingKernel(warpingKernelName));
226  setWarpingKernel(*warpingKernelPtr);
227 }
228 
230  SeparableKernel const &warpingKernel
231 ) {
232  if (_maskWarpingKernelPtr) {
233  _testWarpingKernels(warpingKernel, *_maskWarpingKernelPtr);
234  }
235  PTR(SeparableKernel) warpingKernelPtr(std::static_pointer_cast<SeparableKernel>(warpingKernel.clone()));
236  _testDevicePreference(_devicePreference, warpingKernelPtr);
237  _warpingKernelPtr = warpingKernelPtr;
238 }
239 
240 
241 PTR(afwMath::SeparableKernel) afwMath::WarpingControl::getMaskWarpingKernel() const {
242  if (_maskWarpingKernelPtr) { // lazily update kernel cache
243  if (_maskWarpingKernelPtr->getCacheSize() != _cacheSize) {
244  _maskWarpingKernelPtr->computeCache(_cacheSize);
245  }
246  }
247  return _maskWarpingKernelPtr;
248 }
249 
251  std::string const &maskWarpingKernelName
252 ) {
253  if (!maskWarpingKernelName.empty()) {
254  PTR(SeparableKernel) maskWarpingKernelPtr(makeWarpingKernel(maskWarpingKernelName));
255  setMaskWarpingKernel(*maskWarpingKernelPtr);
256  } else {
257  _maskWarpingKernelPtr.reset();
258  }
259 }
260 
262  SeparableKernel const & maskWarpingKernel
263 ) {
264  _testWarpingKernels(*_warpingKernelPtr, maskWarpingKernel);
265  _maskWarpingKernelPtr = std::static_pointer_cast<SeparableKernel>(maskWarpingKernel.clone());
266 }
267 
268 
270  SeparableKernel const &warpingKernel,
271  SeparableKernel const &maskWarpingKernel
272 ) const {
275  warpingKernel.getDimensions()
276  );
278  lsst::afw::geom::Point2I(0, 0) - lsst::afw::geom::Extent2I(maskWarpingKernel.getCtr()),
279  maskWarpingKernel.getDimensions()
280  );
281  if (!kernelBBox.contains(maskKernelBBox)) {
282  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
283  "warping kernel is smaller than mask warping kernel");
284  }
285 }
286 
288  lsst::afw::gpu::DevicePreference const &devicePreference,
289  CONST_PTR(SeparableKernel) const &warpingKernelPtr
290 ) const {
291  CONST_PTR(LanczosWarpingKernel) const lanczosKernelPtr =
292  std::dynamic_pointer_cast<const LanczosWarpingKernel>(warpingKernelPtr);
293  if (devicePreference == lsst::afw::gpu::USE_GPU && !lanczosKernelPtr) {
294  throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
295  "devicePreference = USE_GPU, but warping kernel not Lanczos");
296  }
297 }
298 
299 
300 
301 template<typename DestExposureT, typename SrcExposureT>
303  DestExposureT &destExposure,
304  SrcExposureT const &srcExposure,
305  afwMath::WarpingControl const &control,
306  typename DestExposureT::MaskedImageT::SinglePixel padValue
307  )
308 {
309  if (!destExposure.hasWcs()) {
310  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "destExposure has no Wcs");
311  }
312  if (!srcExposure.hasWcs()) {
313  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "srcExposure has no Wcs");
314  }
315  typename DestExposureT::MaskedImageT mi = destExposure.getMaskedImage();
316  std::shared_ptr<afwImage::Calib> calibCopy(new afwImage::Calib(*srcExposure.getCalib()));
317  destExposure.setCalib(calibCopy);
318  destExposure.setFilter(srcExposure.getFilter());
319  return warpImage(mi, *destExposure.getWcs(), srcExposure.getMaskedImage(), *srcExposure.getWcs(),
320  control, padValue);
321 }
322 
323 
324 /************************************************************************************************************/
325 namespace {
326 
327  inline afwGeom::Point2D computeSrcPos(
328  int destCol,
329  int destRow,
330  afwGeom::Point2D const &destXY0,
331  afwImage::Wcs const &destWcs,
332  afwImage::Wcs const &srcWcs)
333  {
334  double const col = afwImage::indexToPosition(destCol + destXY0[0]);
335  double const row = afwImage::indexToPosition(destRow + destXY0[1]);
336  afwGeom::Angle sky1, sky2;
337  destWcs.pixelToSky(col, row, sky1, sky2);
338  return srcWcs.skyToPixel(sky1, sky2);
339  }
340 
341 
342  inline double computeRelativeArea(
343  afwGeom::Point2D const &srcPos,
344  afwGeom::Point2D const &leftSrcPos,
345  afwGeom::Point2D const &upSrcPos)
346  {
347  afwGeom::Extent2D dSrcA = srcPos - leftSrcPos;
348  afwGeom::Extent2D dSrcB = srcPos - upSrcPos;
349 
350  return std::abs(dSrcA.getX()*dSrcB.getY() - dSrcA.getY()*dSrcB.getX());
351  }
352 
353  template<typename DestImageT, typename SrcImageT>
354  int doWarpImage(
355  DestImageT &destImage,
356  SrcImageT const &srcImage,
357  afwMath::detail::PositionFunctor const &computeSrcPos,
358  afwMath::WarpingControl const &control,
360  typename DestImageT::SinglePixel padValue
361  ) {
362  if (afwMath::details::isSameObject(destImage, srcImage)) {
363  throw LSST_EXCEPT(pexExcept::InvalidParameterError,
364  "destImage is srcImage; cannot warp in place");
365  }
366  if (destImage.getBBox(afwImage::LOCAL).isEmpty()) {
367  return 0;
368  }
369  // if src image is too small then don't try to warp
370  try {
371  PTR(afwMath::SeparableKernel) warpingKernelPtr = control.getWarpingKernel();
372  warpingKernelPtr->shrinkBBox(srcImage.getBBox(afwImage::LOCAL));
373  } catch(...) {
374  for (int y = 0, height = destImage.getHeight(); y < height; ++y) {
375  for (typename DestImageT::x_iterator destPtr = destImage.row_begin(y), end = destImage.row_end(y);
376  destPtr != end; ++destPtr) {
377  *destPtr = padValue;
378  }
379  }
380  return 0;
381  }
382  PTR(afwMath::SeparableKernel) warpingKernelPtr = control.getWarpingKernel();
383  int interpLength = control.getInterpLength();
384  lsst::afw::gpu::DevicePreference devPref = control.getDevicePreference();
385 
386  std::shared_ptr<afwMath::LanczosWarpingKernel const> const lanczosKernelPtr =
387  std::dynamic_pointer_cast<afwMath::LanczosWarpingKernel>(warpingKernelPtr);
388 
389  if (lsst::afw::gpu::isGpuEnabled()) {
390  if(!lanczosKernelPtr) {
391  if (devPref == lsst::afw::gpu::USE_GPU) {
392  throw LSST_EXCEPT(pexExcept::InvalidParameterError, "Gpu can process only Lanczos kernels");
393  }
394  } else if (devPref == lsst::afw::gpu::USE_GPU || (lsst::afw::gpu::isGpuBuild() && interpLength > 0) ) {
395  PTR(afwMath::SeparableKernel) maskWarpingKernelPtr = control.getWarpingKernel();
396  if (control.getMaskWarpingKernel() )
397  maskWarpingKernelPtr = control.getMaskWarpingKernel();
398  if (devPref == lsst::afw::gpu::AUTO_WITH_CPU_FALLBACK) {
399  try {
400  std::pair<int, afwMath::detail::WarpImageGpuStatus::ReturnCode> result =
401  afwMath::detail::warpImageGPU(destImage, srcImage,
402  *lanczosKernelPtr, *maskWarpingKernelPtr,
403  computeSrcPos, interpLength, padValue, false);
404  if (result.second == afwMath::detail::WarpImageGpuStatus::OK) return result.first;
405  }
406  catch(lsst::afw::gpu::GpuMemoryError) { }
407  catch(pexExcept::MemoryError) { }
408  catch(lsst::afw::gpu::GpuRuntimeError) { }
409  } else if (devPref != lsst::afw::gpu::USE_CPU) {
410  std::pair<int, afwMath::detail::WarpImageGpuStatus::ReturnCode> result =
411  afwMath::detail::warpImageGPU(destImage, srcImage,
412  *lanczosKernelPtr, *maskWarpingKernelPtr,
413  computeSrcPos, interpLength, padValue,
414  devPref == lsst::afw::gpu::USE_GPU);
415  if (result.second == afwMath::detail::WarpImageGpuStatus::OK) return result.first;
416  if (devPref == lsst::afw::gpu::USE_GPU) {
417  throw LSST_EXCEPT(pexExcept::RuntimeError,
418  "Gpu cannot perform this warp (kernel too big?)");
419  }
420  }
421  }
422  }
423 
424  int numGoodPixels = 0;
425 
426  // Get the source MaskedImage and a pixel accessor to it.
427  int const srcWidth = srcImage.getWidth();
428  int const srcHeight = srcImage.getHeight();
429  LOGL_DEBUG("TRACE2.afw.math.warp", "source image width=%d; height=%d", srcWidth, srcHeight);
430 
431  int const destWidth = destImage.getWidth();
432  int const destHeight = destImage.getHeight();
433 
434  LOGL_DEBUG("TRACE2.afw.math.warp", "remap image width=%d; height=%d", destWidth, destHeight);
435 
436  // Set each pixel of destExposure's MaskedImage
437  LOGL_DEBUG("TRACE3.afw.math.warp", "Remapping masked image");
438 
439  // A cache of pixel positions on the source corresponding to the previous or current row
440  // of the destination image.
441  // The first value is for column -1 because the previous source position is used to compute relative area
442  // To simplify the indexing, use an iterator that starts at begin+1, thus:
443  // srcPosView = _srcPosList.begin() + 1
444  // srcPosView[col-1] and lower indices are for this row
445  // srcPosView[col] and higher indices are for the previous row
446  std::vector<afwGeom::Point2D> _srcPosList(1 + destWidth);
447  std::vector<afwGeom::Point2D>::iterator const srcPosView = _srcPosList.begin() + 1;
448 
449  int const maxCol = destWidth - 1;
450  int const maxRow = destHeight - 1;
451 
452  afwMath::detail::WarpAtOnePoint<DestImageT, SrcImageT> warpAtOnePoint(srcImage, control, padValue);
453 
454  if (interpLength > 0) {
455  // Use interpolation. Note that 1 produces the same result as no interpolation
456  // but uses this code branch, thus providing an easy way to compare the two branches.
457 
458  // Estimate for number of horizontal interpolation band edges, to reserve memory in vectors
459  int const numColEdges = 2 + ((destWidth - 1) / interpLength);
460 
461  // A list of edge column indices for interpolation bands;
462  // starts at -1, increments by interpLen (except the final interval), and ends at destWidth-1
463  std::vector<int> edgeColList;
464  edgeColList.reserve(numColEdges);
465 
466  // A list of 1/column width for horizontal interpolation bands; the first value is garbage.
467  // The inverse is used for speed because the values is always multiplied.
468  std::vector<double> invWidthList;
469  invWidthList.reserve(numColEdges);
470 
471  // Compute edgeColList and invWidthList
472  edgeColList.push_back(-1);
473  invWidthList.push_back(0.0);
474  for (int prevEndCol = -1; prevEndCol < maxCol; prevEndCol += interpLength) {
475  int endCol = prevEndCol + interpLength;
476  if (endCol > maxCol) {
477  endCol = maxCol;
478  }
479  edgeColList.push_back(endCol);
480  assert(endCol - prevEndCol > 0);
481  invWidthList.push_back(1.0 / static_cast<double>(endCol - prevEndCol));
482  }
483  assert(edgeColList.back() == maxCol);
484 
485  // A list of delta source positions along the edge columns of the horizontal interpolation bands
486  std::vector<afwGeom::Extent2D> yDeltaSrcPosList(edgeColList.size());
487 
488  // Initialize _srcPosList for row -1
489  //srcPosView[-1] = computeSrcPos(-1, -1, destXY0, destWcs, srcWcs);
490  srcPosView[-1] = computeSrcPos(-1, -1);
491  for (int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
492  int const prevEndCol = edgeColList[colBand-1];
493  int const endCol = edgeColList[colBand];
494  afwGeom::Point2D leftSrcPos = srcPosView[prevEndCol];
495  afwGeom::Point2D rightSrcPos = computeSrcPos(endCol, -1);
496  afwGeom::Extent2D xDeltaSrcPos = (rightSrcPos - leftSrcPos) * invWidthList[colBand];
497 
498  for (int col = prevEndCol + 1; col <= endCol; ++col) {
499  srcPosView[col] = srcPosView[col-1] + xDeltaSrcPos;
500  }
501  }
502 
503  int endRow = -1;
504  while (endRow < maxRow) {
505  // Next horizontal interpolation band
506 
507  int prevEndRow = endRow;
508  endRow = prevEndRow + interpLength;
509  if (endRow > maxRow) {
510  endRow = maxRow;
511  }
512  assert(endRow - prevEndRow > 0);
513  double interpInvHeight = 1.0 / static_cast<double>(endRow - prevEndRow);
514 
515  // Set yDeltaSrcPosList for this horizontal interpolation band
516  for (int colBand = 0, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
517  int endCol = edgeColList[colBand];
518  afwGeom::Point2D bottomSrcPos = computeSrcPos(endCol, endRow);
519  yDeltaSrcPosList[colBand] = (bottomSrcPos - srcPosView[endCol]) * interpInvHeight;
520  }
521 
522  for (int row = prevEndRow + 1; row <= endRow; ++row) {
523  typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
524  srcPosView[-1] += yDeltaSrcPosList[0];
525  for (int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
527 
528  int const prevEndCol = edgeColList[colBand-1];
529  int const endCol = edgeColList[colBand];
530 
531  // Compute xDeltaSrcPos; remember that srcPosView contains
532  // positions for this row in prevEndCol and smaller indices,
533  // and positions for the previous row for larger indices (including endCol)
534  afwGeom::Point2D leftSrcPos = srcPosView[prevEndCol];
535  afwGeom::Point2D rightSrcPos = srcPosView[endCol] + yDeltaSrcPosList[colBand];
536  afwGeom::Extent2D xDeltaSrcPos = (rightSrcPos - leftSrcPos) * invWidthList[colBand];
537 
538  for (int col = prevEndCol + 1; col <= endCol; ++col, ++destXIter) {
539  afwGeom::Point2D leftSrcPos = srcPosView[col-1];
540  afwGeom::Point2D srcPos = leftSrcPos + xDeltaSrcPos;
541  double relativeArea = computeRelativeArea(srcPos, leftSrcPos, srcPosView[col]);
542 
543  srcPosView[col] = srcPos;
544 
545  if (warpAtOnePoint(destXIter, srcPos, relativeArea,
547  ++numGoodPixels;
548  }
549  } // for col
550  } // for col band
551  } // for row
552  } // while next row band
553 
554 
555  } else {
556  // No interpolation
557 
558  // initialize _srcPosList for row -1;
559  // the first value is not needed, but it's safer to compute it
560  std::vector<afwGeom::Point2D>::iterator srcPosView = _srcPosList.begin() + 1;
561  for (int col = -1; col < destWidth; ++col) {
562  srcPosView[col] = computeSrcPos(col, -1);
563  }
564 
565  for (int row = 0; row < destHeight; ++row) {
566  typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
567 
568  srcPosView[-1] = computeSrcPos(-1, row);
569 
570  for (int col = 0; col < destWidth; ++col, ++destXIter) {
571  afwGeom::Point2D srcPos = computeSrcPos(col, row);
572  double relativeArea = computeRelativeArea(srcPos, srcPosView[col-1], srcPosView[col]);
573  srcPosView[col] = srcPos;
574 
575  if (warpAtOnePoint(destXIter, srcPos, relativeArea,
577  ++numGoodPixels;
578  }
579  } // for col
580  } // for row
581  } // if interp
582 
583  return numGoodPixels;
584  }
585 
586 } // namespace
587 
588 template<typename DestImageT, typename SrcImageT>
590  DestImageT &destImage,
591  lsst::afw::image::Wcs const &destWcs,
592  SrcImageT const &srcImage,
593  lsst::afw::image::Wcs const &srcWcs,
594  afwMath::WarpingControl const &control,
595  typename DestImageT::SinglePixel padValue
596 ) {
597  afwGeom::Point2D const destXY0(destImage.getXY0());
598  afwImage::XYTransformFromWcsPair xyTransform{destWcs.clone(), srcWcs.clone()};
599  afwMath::detail::XYTransformPositionFunctor const computeSrcPos{destXY0, xyTransform};
600  return doWarpImage(destImage, srcImage, computeSrcPos, control, padValue);
601 }
602 
603 
604 template<typename DestImageT, typename SrcImageT>
606  DestImageT &destImage,
607  SrcImageT const &srcImage,
608  afwGeom::XYTransform const &xyTransform,
609  afwMath::WarpingControl const &control,
610  typename DestImageT::SinglePixel padValue
611 ) {
612  afwGeom::Point2D const destXY0(destImage.getXY0());
613  afwMath::detail::XYTransformPositionFunctor const computeSrcPos(destXY0, xyTransform);
614  return doWarpImage(destImage, srcImage, computeSrcPos, control, padValue);
615 }
616 
617 
618 template<typename DestImageT, typename SrcImageT>
620  DestImageT &destImage,
621  SrcImageT const &srcImage,
622  afwGeom::LinearTransform const &linearTransform,
623  afwGeom::Point2D const &centerPosition,
624  afwMath::WarpingControl const &control,
625  typename DestImageT::SinglePixel padValue
626 ) {
627  // force src and dest to be the same size and xy0
628  if (
629  (destImage.getWidth() != srcImage.getWidth()) ||
630  (destImage.getHeight() != srcImage.getHeight()) ||
631  (destImage.getXY0() != srcImage.getXY0())
632  ) {
633  std::ostringstream errStream;
634  errStream << "src and dest images must have same size and xy0.";
635  throw LSST_EXCEPT(pexExcept::InvalidParameterError, errStream.str());
636  }
637 
638  // set the xy0 coords to 0,0 to make life easier
639  SrcImageT srcImageCopy(srcImage, true);
640  srcImageCopy.setXY0(0, 0);
641  destImage.setXY0(0, 0);
642  afwGeom::Extent2D cLocal = afwGeom::Extent2D(centerPosition) - afwGeom::Extent2D(srcImage.getXY0());
643 
644  // for the affine transform, the centerPosition will not only get sheared, but also
645  // moved slightly. So we'll include a translation to move it back by an amount
646  // centerPosition - translatedCenterPosition
647  afwGeom::AffineTransform affTran(linearTransform, cLocal - linearTransform(cLocal));
648  afwGeom::AffineXYTransform affXYTransform(affTran);
649 
650  // now warp
651 #if 0
652  static float t = 0.0;
653  float t_before = 1.0*clock()/CLOCKS_PER_SEC;
654  int n = warpImage(destImage, srcImageCopy, affTran, control, padValue);
655  float t_after = 1.0*clock()/CLOCKS_PER_SEC;
656  float dt = t_after - t_before;
657  t += dt;
658  std::cout <<srcImage.getWidth()<<"x"<<srcImage.getHeight()<<": "<< dt <<" "<< t <<std::endl;
659 #else
660  int n = warpImage(destImage, srcImageCopy, affXYTransform, control, padValue);
661 #endif
662 
663  // fix the origin and we're done.
664  destImage.setXY0(srcImage.getXY0());
665 
666  return n;
667 }
668 
669 
670 //
671 // Explicit instantiations
672 //
674 // may need to omit default params for EXPOSURE -- original code did that and it worked
675 #define EXPOSURE(PIXTYPE) afwImage::Exposure<PIXTYPE, afwImage::MaskPixel, afwImage::VariancePixel>
676 #define MASKEDIMAGE(PIXTYPE) afwImage::MaskedImage<PIXTYPE, afwImage::MaskPixel, afwImage::VariancePixel>
677 #define IMAGE(PIXTYPE) afwImage::Image<PIXTYPE>
678 #define NL /* */
679 
680 #define INSTANTIATE(DESTIMAGEPIXELT, SRCIMAGEPIXELT) \
681  template int afwMath::warpCenteredImage( \
682  IMAGE(DESTIMAGEPIXELT) &destImage, \
683  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
684  afwGeom::LinearTransform const &linearTransform, \
685  afwGeom::Point2D const &centerPosition, \
686  afwMath::WarpingControl const &control, \
687  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
688  template int afwMath::warpCenteredImage( \
689  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
690  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
691  afwGeom::LinearTransform const &linearTransform, \
692  afwGeom::Point2D const &centerPosition, \
693  afwMath::WarpingControl const &control, \
694  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
695  template int afwMath::warpImage( \
696  IMAGE(DESTIMAGEPIXELT) &destImage, \
697  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
698  afwGeom::XYTransform const &xyTransform, \
699  afwMath::WarpingControl const &control, \
700  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
701  template int afwMath::warpImage( \
702  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
703  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
704  afwGeom::XYTransform const &xyTransform, \
705  afwMath::WarpingControl const &control, \
706  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
707  template int afwMath::warpImage( \
708  IMAGE(DESTIMAGEPIXELT) &destImage, \
709  afwImage::Wcs const &destWcs, \
710  IMAGE(SRCIMAGEPIXELT) const &srcImage, \
711  afwImage::Wcs const &srcWcs, \
712  afwMath::WarpingControl const &control, \
713  IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
714  template int afwMath::warpImage( \
715  MASKEDIMAGE(DESTIMAGEPIXELT) &destImage, \
716  afwImage::Wcs const &destWcs, \
717  MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
718  afwImage::Wcs const &srcWcs, \
719  afwMath::WarpingControl const &control, \
720  MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); NL \
721  template int afwMath::warpExposure( \
722  EXPOSURE(DESTIMAGEPIXELT) &destExposure, \
723  EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, \
724  afwMath::WarpingControl const &control,\
725  EXPOSURE(DESTIMAGEPIXELT)::MaskedImageT::SinglePixel padValue);
726 
727 
728 
729 
730 INSTANTIATE(double, double)
731 INSTANTIATE(double, float)
732 INSTANTIATE(double, int)
733 INSTANTIATE(double, std::uint16_t)
734 INSTANTIATE(float, float)
735 INSTANTIATE(float, int)
736 INSTANTIATE(float, std::uint16_t)
737 INSTANTIATE(int, int)
738 INSTANTIATE(std::uint16_t, std::uint16_t)
int y
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.
boost::shared_ptr< SeparableKernel > makeWarpingKernel(std::string name)
Return a warping kernel given its name.
geom::Extent2I const getDimensions() const
Return the Kernel&#39;s dimensions (width, height)
Definition: Kernel.h:223
double indexToPosition(double ind)
Convert image index to image position.
Definition: ImageUtils.h:54
table::Key< std::string > name
Definition: ApCorrMap.cc:71
#define LOGL_DEBUG(logger, message...)
Definition: Log.h:513
bool contains(Point2I const &point) const
Return true if the box contains the point.
additional GPU exceptions
geom::Point2D skyToPixel(geom::Angle sky1, geom::Angle sky2) const
Convert from sky coordinates (e.g. RA/dec) to pixel positions.
Definition: Wcs.cc:796
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
Definition: Kernel.h:983
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
XYTransformFromWcsPair: An XYTransform obtained by putting two Wcs objects &quot;back to back&quot;...
Definition: Wcs.h:449
Parameters to control convolution.
Definition: warpExposure.h:238
Implementation of the WCS standard for a any projection.
Definition: Wcs.h:107
virtual Ptr clone(void) const
Definition: Wcs.cc:566
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 pixel position to sky coordinates (e.g. RA/dec)
Definition: Wcs.cc:886
#define CONST_PTR(...)
Definition: base.h:47
#define INSTANTIATE(T)
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:237
A 2D linear coordinate transformation.
std::pair< int, WarpImageGpuStatus::ReturnCode > warpImageGPU(DestImageT &destImage, SrcImageT const &srcImage, afwMath::LanczosWarpingKernel const &lanczosKernel, lsst::afw::math::SeparableKernel const &maskWarpingKernel, PositionFunctor const &computeSrcPos, int const interpLength, typename DestImageT::SinglePixel padValue, const bool forceProcessing)
GPU accelerated image warping using Lanczos resampling.
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.
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:532
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
if(width!=gim.getWidth()||height!=gim.getHeight()||x0!=gim.getX0()||y0!=gim.getY0())
Definition: saturated.cc:47
Interface for CPU/GPU device selection.
double x
lsst::afw::geom::Point2I getCtr() const
Return index of kernel&#39;s center.
Definition: Kernel.h:251
Bilinear warping: fast; good for undersampled data.
Definition: warpExposure.h:100
virtual void setKernelParameter(unsigned int ind, double value) const
Set one kernel parameter.
int row
Definition: CR.cc:159
#define LSST_EXCEPT(type,...)
Definition: Exception.h:46
tbl::Key< std::string > warpingKernelName
Definition: CoaddPsf.cc:329
Support for warping an image to a new WCS.
Virtual base class for 2D transforms.
Definition: XYTransform.h:48
#define PTR(...)
Definition: base.h:41
virtual void setKernelParameter(unsigned int ind, double value) const
Set one kernel parameter.
Nearest neighbor warping: fast; good for undersampled data.
Definition: warpExposure.h:156
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:71
ImageT::image_category image_category
Definition: Image.h:79
Extent< double, 2 > Extent2D
Definition: Extent.h:361
GPU accelerared image warping.
virtual boost::shared_ptr< Kernel > clone() const
Return a pointer to a deep copy of this kernel.
Include files required for standard LSST Exception handling.
Wrap an AffineTransform.
Definition: XYTransform.h:152
Kernels are used for convolution with MaskedImages and (eventually) Images.
Definition: Kernel.h:131
int col
Definition: CR.cc:158
A function to determine whether compiling for GPU is enabled.
Functions to handle coordinates.