opencv 仿射变换代码解析 warpPerspective(),remap()

void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

原理说明
参数说明:

  • InputArray src:输入的图像
  • OutputArray dst:输出的图像
  • InputArray M:透视变换的矩阵
  • Size dsize:输出图像的大小
  • int flags=INTER_LINEAR:输出图像的插值方法,
  • int borderMode=BORDER_CONSTANT:图像边界的处理方式 (BORDER_CONSTANT用指定像素值填充边缘)
  • const Scalar& borderValue=Scalar():边界的颜色设置,一般默认是0
// 该段代码来自:https://blog.csdn.net/fengbingchun/article/details/52004083
// 亲测无误
// fbc_cv is free software and uses the same licence as OpenCV
// Email: fengbingchun@163.com
 
#ifndef FBC_CV_WARP_PERSPECTIVE_HPP_
#define FBC_CV_WARP_PERSPECTIVE_HPP_
 
/* reference: include/opencv2/imgproc.hpp
              modules/imgproc/src/imgwarp.cpp
*/
 
#include <typeinfo>
#include "core/mat.hpp"
#include "core/invert.hpp"
#include "imgproc.hpp"
#include "remap.hpp"
 
namespace fbc {
 
// Calculates a perspective transform from four pairs of the corresponding points
FBC_EXPORTS int getPerspectiveTransform(const Point2f src1[], const Point2f src2[], Mat_<double, 1>& dst);
 
// Applies a perspective transformation to an image
// The function cannot operate in - place
// support type: uchar/float
/* 
\f[\texttt{ dst } (x, y) = \texttt{ src } \left(\frac{ M_{ 11 } x + M_{ 12 } y + M_{ 13 } }{M_{ 31 } x + M_{ 32 } y + M_{ 33 }},
	\frac{ M_{ 21 } x + M_{ 22 } y + M_{ 23 } }{M_{ 31 } x + M_{ 32 } y + M_{ 33 }} \right)\f]
*/
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
int warpPerspective(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst, const Mat_<_Tp2, chs2>& M_,
	int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar())
{
	FBC_Assert(src.data != NULL && dst.data != NULL && M_.data != NULL);
	FBC_Assert(src.cols > 0 && src.rows > 0 && dst.cols > 0 && dst.rows > 0);
	FBC_Assert(src.data != dst.data);
	FBC_Assert(typeid(double).name() == typeid(_Tp2).name() && M_.rows == 3 && M_.cols == 3);
	FBC_Assert((typeid(uchar).name() == typeid(_Tp1).name()) || (typeid(float).name() == typeid(_Tp1).name())); // uchar/float
 
	double M[9];
	Mat_<double, 1> matM(3, 3, M);
 
	int interpolation = flags & INTER_MAX;
	if (interpolation == INTER_AREA)
		interpolation = INTER_LINEAR;
 
	if (!(flags & WARP_INVERSE_MAP))
		invert(M_, matM);
 
	Range range(0, dst.rows);
 
	const int BLOCK_SZ = 32; //===运行优化所需,划分成小块处理
	short XY[BLOCK_SZ*BLOCK_SZ * 2], A[BLOCK_SZ*BLOCK_SZ];//=== XY dst每个像素对应src上的坐标;A在非最邻近差值模式下起作用,为运行优化处理块中的位置索引
	int x, y, x1, y1, width = dst.cols, height = dst.rows;
 
	int bh0 = std::min(BLOCK_SZ / 2, height);//=== 正常情况,XY对应的像素块是w*h=64*16
	int bw0 = std::min(BLOCK_SZ*BLOCK_SZ / bh0, width);
	bh0 = std::min(BLOCK_SZ*BLOCK_SZ / bw0, height);
 
	for (y = range.start; y < range.end; y += bh0) {
		for (x = 0; x < width; x += bw0) {
			int bw = std::min(bw0, width - x);
			int bh = std::min(bh0, range.end - y); // height
 
			Mat_<short, 2> _XY(bh, bw, XY), matA;
			Mat_<_Tp1, chs1> dpart;
			dst.getROI(dpart, Rect(x, y, bw, bh));
 
			for (y1 = 0; y1 < bh; y1++) {
				short* xy = XY + y1*bw * 2;
				double X0 = M[0] * x + M[1] * (y + y1) + M[2];
				double Y0 = M[3] * x + M[4] * (y + y1) + M[5];
				double W0 = M[6] * x + M[7] * (y + y1) + M[8];
 
				if (interpolation == INTER_NEAREST) {
					x1 = 0;
					for (; x1 < bw; x1++) {
						double W = W0 + M[6] * x1;
						W = W ? 1. / W : 0;
						double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0] * x1)*W));
						double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3] * x1)*W));
						int X = saturate_cast<int>(fX);//===saturate_cast防止溢出
						int Y = saturate_cast<int>(fY);
 
						xy[x1 * 2] = saturate_cast<short>(X);
						xy[x1 * 2 + 1] = saturate_cast<short>(Y);
					}
				} else {
					short* alpha = A + y1*bw;
					x1 = 0;
					for (; x1 < bw; x1++) {
						double W = W0 + M[6] * x1;
						W = W ? INTER_TAB_SIZE / W : 0;
						double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0] * x1)*W));
						double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3] * x1)*W));
						int X = saturate_cast<int>(fX);
						int Y = saturate_cast<int>(fY);
 
						xy[x1 * 2] = saturate_cast<short>(X >> INTER_BITS);//===dst每个像素对应src上的坐标-x
						xy[x1 * 2 + 1] = saturate_cast<short>(Y >> INTER_BITS);//===dst每个像素对应src上的坐标-y
						alpha[x1] = (short)((Y & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (X & (INTER_TAB_SIZE - 1)));//===在非最邻近差值模式下起作用,为运行优化处理块中的位置索引
					}
				}
			}
 
			if (interpolation == INTER_NEAREST) {
				remap(src, dpart, _XY, Mat_<float, 1>(), interpolation, borderMode, borderValue);
			} else {
				Mat_<ushort, 1> _matA(bh, bw, A);
				remap(src, dpart, _XY, _matA, interpolation, borderMode, borderValue);
			}
		}
	}
 
	return 0;
}
 
} // namespace fbc
 
#endif // FBC_CV_WARP_PERSPECTIVE_HPP_

void remap( InputArray src, OutputArray dst,
                         InputArray map1, InputArray map2,
                         int interpolation, int borderMode = BORDER_CONSTANT,
                         const Scalar& borderValue = Scalar());

一篇不错的参考

参数说明

  • src,输入图像,即源图像,填灰度图或真彩图均可。
  • dst,输出图像,需要和源图像有一样的尺寸和类型。
  • map1,即 mapx。
  • map2,即 mapy 。
  • interpolation,插值方法。插值就是根据已知数据点(条件),来预测未知数据点值得方法。在重映射过程中,图像的大小可能发生改变。此时像素与像素之间的关系就不是一一对应关系,因此在重映射过程中,可能会涉及到像素值的插值计算。可选插值方式如下:

INTER_NEAREST(最近邻差值)

INTER_LINEAR(双线性插值,一般选择这种方式)

INTER_CUBIC(双三次样条插值,超过 4×4 像素邻域内的双三次插值)

INTER_LANCZOS4(Lanczos 插值,超过 8×8 像素邻域的 Lanczos 插值)

  • borderValue ,只有当 borderMode取值为 BORDER_CONSTANT 时,这个参数才会被使用,边界会被填充成 borderValue 指定的颜色。
// 代码源链接:https://blog.csdn.net/fengbingchun/article/details/51872436
// fbc_cv is free software and uses the same licence as OpenCV
// Email: fengbingchun@163.com
 
#ifndef FBC_CV_REMAP_HPP_
#define FBC_CV_REMAP_HPP_
 
/* reference: include/opencv2/imgproc.hpp
              modules/imgproc/src/imgwarp.cpp
*/
 
#include <typeinfo>
#include "core/mat.hpp"
#include "core/base.hpp"
#include "core/core.hpp"
#include "imgproc.hpp"
#include "resize.hpp"
 
namespace fbc {
 
const int INTER_REMAP_COEF_BITS = 15;
const int INTER_REMAP_COEF_SCALE = 1 << INTER_REMAP_COEF_BITS;
 
static uchar NNDeltaTab_i[INTER_TAB_SIZE2][2];
 
static float BilinearTab_f[INTER_TAB_SIZE2][2][2];
static short BilinearTab_i[INTER_TAB_SIZE2][2][2];
 
static float BicubicTab_f[INTER_TAB_SIZE2][4][4];
static short BicubicTab_i[INTER_TAB_SIZE2][4][4];
 
static float Lanczos4Tab_f[INTER_TAB_SIZE2][8][8];
static short Lanczos4Tab_i[INTER_TAB_SIZE2][8][8];
 
template<typename _Tp1, typename _Tp2, int chs1, int chs2> static int remap_nearest(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue);
template<typename _Tp1, typename _Tp2, int chs1, int chs2> static int remap_linear(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue);
template<typename _Tp1, typename _Tp2, int chs1, int chs2> static int remap_cubic(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue);
template<typename _Tp1, typename _Tp2, int chs1, int chs2> static int remap_lanczos4(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue);
 
// Applies a generic geometrical transformation to an image
// transforms the source image using the specified map, this function cannot operate in-place
// support type: uchar/float
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
int remap(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst, const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2,
	int interpolation, int borderMode = BORDER_CONSTANT, const Scalar& borderValue = Scalar())
{
	FBC_Assert(map1.size().area() > 0 && map1.size() == map2.size());
	FBC_Assert(map1.data != NULL && map2.data != NULL);
	FBC_Assert(src.size() == map1.size() && src.size() == dst.size());
	FBC_Assert(src.data != dst.data);
	FBC_Assert(typeid(uchar).name() == typeid(_Tp1).name() || typeid(float).name() == typeid(_Tp1).name()); // uchar || float
	FBC_Assert(typeid(float).name() == typeid(_Tp2).name());
	FBC_Assert(chs2 == 1);
 
	switch (interpolation) {
		case 0: {
			remap_nearest(src, dst, map1, map2, borderMode, borderValue);
			break;
		}
		case 1:
		case 3: {
			remap_linear(src, dst, map1, map2, borderMode, borderValue);
			break;
		}
		case 2: {
			remap_cubic(src, dst, map1, map2, borderMode, borderValue);
			break;
		}
		case 4: {
			remap_lanczos4(src, dst, map1, map2, borderMode, borderValue);
			break;
		}
		default:
			return -1;
	}
 
	return 0;
}
 
template<typename _Tp>
static inline void interpolateLinear(_Tp x, _Tp* coeffs)
{
	coeffs[0] = 1.f - x;
	coeffs[1] = x;
}
 
template<typename _Tp>
static void initInterTab1D(int method, float* tab, int tabsz)
{
	float scale = 1.f / tabsz;
	if (method == INTER_LINEAR) {
		for (int i = 0; i < tabsz; i++, tab += 2)
			interpolateLinear<float>(i*scale, tab);
	} else if (method == INTER_CUBIC) {
		for (int i = 0; i < tabsz; i++, tab += 4)
			interpolateCubic<float>(i*scale, tab);
	} else if (method == INTER_LANCZOS4) {
		for (int i = 0; i < tabsz; i++, tab += 8)
			interpolateLanczos4<float>(i*scale, tab);
	} else {
		FBC_Error("Unknown interpolation method");
	}
}
 
template<typename _Tp>
static const void* initInterTab2D(int method, bool fixpt)
{
	static bool inittab[INTER_MAX + 1] = { false };
	float* tab = 0;
	short* itab = 0;
	int ksize = 0;
	if (method == INTER_LINEAR) {
		tab = BilinearTab_f[0][0], itab = BilinearTab_i[0][0], ksize = 2;
	} else if (method == INTER_CUBIC) {
		tab = BicubicTab_f[0][0], itab = BicubicTab_i[0][0], ksize = 4;
	} else if (method == INTER_LANCZOS4) {
		tab = Lanczos4Tab_f[0][0], itab = Lanczos4Tab_i[0][0], ksize = 8;
	} else {
		FBC_Error("Unknown/unsupported interpolation type");
	}
 
	if (!inittab[method]) {
		AutoBuffer<float> _tab(8 * INTER_TAB_SIZE);
		int i, j, k1, k2;
		initInterTab1D<float>(method, _tab, INTER_TAB_SIZE);
		for (i = 0; i < INTER_TAB_SIZE; i++) {
			for (j = 0; j < INTER_TAB_SIZE; j++, tab += ksize*ksize, itab += ksize*ksize) {
				int isum = 0;
				NNDeltaTab_i[i*INTER_TAB_SIZE + j][0] = j < INTER_TAB_SIZE / 2;
				NNDeltaTab_i[i*INTER_TAB_SIZE + j][1] = i < INTER_TAB_SIZE / 2;
 
				for (k1 = 0; k1 < ksize; k1++) {
					float vy = _tab[i*ksize + k1];
					for (k2 = 0; k2 < ksize; k2++) {
						float v = vy*_tab[j*ksize + k2];
						tab[k1*ksize + k2] = v;
						isum += itab[k1*ksize + k2] = saturate_cast<short>(v*INTER_REMAP_COEF_SCALE);
					}
				}
 
				if (isum != INTER_REMAP_COEF_SCALE) {
					int diff = isum - INTER_REMAP_COEF_SCALE;
					int ksize2 = ksize / 2, Mk1 = ksize2, Mk2 = ksize2, mk1 = ksize2, mk2 = ksize2;
					for (k1 = ksize2; k1 < ksize2 + 2; k1++) {
						for (k2 = ksize2; k2 < ksize2 + 2; k2++) {
							if (itab[k1*ksize + k2] < itab[mk1*ksize + mk2])
								mk1 = k1, mk2 = k2;
							else if (itab[k1*ksize + k2] > itab[Mk1*ksize + Mk2])
								Mk1 = k1, Mk2 = k2;
						}
					}
					if (diff < 0)
						itab[Mk1*ksize + Mk2] = (short)(itab[Mk1*ksize + Mk2] - diff);
					else
						itab[mk1*ksize + mk2] = (short)(itab[mk1*ksize + mk2] - diff);
				}
			}
		}
		tab -= INTER_TAB_SIZE2*ksize*ksize;
		itab -= INTER_TAB_SIZE2*ksize*ksize;
		inittab[method] = true;
	}
 
	return fixpt ? (const void*)itab : (const void*)tab;
}
 
template<typename _Tp>
static bool initAllInterTab2D()
{
	return  initInterTab2D<uchar>(INTER_LINEAR, false) &&
		initInterTab2D<uchar>(INTER_LINEAR, true) &&
		initInterTab2D<uchar>(INTER_CUBIC, false) &&
		initInterTab2D<uchar>(INTER_CUBIC, true) &&
		initInterTab2D<uchar>(INTER_LANCZOS4, false) &&
		initInterTab2D<uchar>(INTER_LANCZOS4, true);
}
 
static volatile bool doInitAllInterTab2D = initAllInterTab2D<uchar>();
 
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
static void remapNearest(const Mat_<_Tp1, chs1>& _src, Mat_<_Tp1, chs1>& _dst, const Mat_<_Tp2, chs2>& _xy, int borderType, const Scalar& _borderValue)
{
	Size ssize = _src.size(), dsize = _dst.size();
	int cn = _src.channels;
	const _Tp1* S0 = (const _Tp1*)_src.ptr();
	size_t sstep = _src.step / sizeof(S0[0]);
	Scalar_<_Tp1> cval(saturate_cast<_Tp1>(_borderValue[0]), saturate_cast<_Tp1>(_borderValue[1]), saturate_cast<_Tp1>(_borderValue[2]), saturate_cast<_Tp1>(_borderValue[3]));
	int dx, dy;
 
	unsigned width1 = ssize.width, height1 = ssize.height;
 
	for (dy = 0; dy < dsize.height; dy++) {
		_Tp1* D = (_Tp1*)_dst.ptr(dy);
		const short* XY = (const short*)_xy.ptr(dy);
 
		if (cn == 1) {
			for (dx = 0; dx < dsize.width; dx++) {
				int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
				if ((unsigned)sx < width1 && (unsigned)sy < height1) {
					D[dx] = S0[sy*sstep + sx];
				} else {
					if (borderType == BORDER_REPLICATE) {
						sx = clip<int>(sx, 0, ssize.width);
						sy = clip<int>(sy, 0, ssize.height);
						D[dx] = S0[sy*sstep + sx];
					} else if (borderType == BORDER_CONSTANT) {
						D[dx] = cval[0];
					} else if (borderType != BORDER_TRANSPARENT) {
						sx = borderInterpolate(sx, ssize.width, borderType);
						sy = borderInterpolate(sy, ssize.height, borderType);
						D[dx] = S0[sy*sstep + sx];
					}
				}
			}
		} else {
			for (dx = 0; dx < dsize.width; dx++, D += cn) {
				int sx = XY[dx * 2], sy = XY[dx * 2 + 1], k;
				const _Tp1 *S;
				if ((unsigned)sx < width1 && (unsigned)sy < height1) {
					if (cn == 3) {
						S = S0 + sy*sstep + sx * 3;
						D[0] = S[0], D[1] = S[1], D[2] = S[2];
					} else if (cn == 4) {
						S = S0 + sy*sstep + sx * 4;
						D[0] = S[0], D[1] = S[1], D[2] = S[2], D[3] = S[3];
					} else {
						S = S0 + sy*sstep + sx*cn;
						for (k = 0; k < cn; k++)
							D[k] = S[k];
					}
				} else if (borderType != BORDER_TRANSPARENT) {
					if (borderType == BORDER_REPLICATE) {
						sx = clip<int>(sx, 0, ssize.width);
						sy = clip<int>(sy, 0, ssize.height);
						S = S0 + sy*sstep + sx*cn;
					} else if (borderType == BORDER_CONSTANT) {
						S = &cval[0];
					} else {
						sx = borderInterpolate(sx, ssize.width, borderType);
						sy = borderInterpolate(sy, ssize.height, borderType);
						S = S0 + sy*sstep + sx*cn;
					}
					for (k = 0; k < cn; k++)
						D[k] = S[k];
				}
			}
		}
	}
}
 
template<class CastOp, typename AT, typename _Tp1, typename _Tp2, typename _Tp3, int chs1, int chs2, int chs3>
static int remapBilinear(const Mat_<_Tp1, chs1>& _src, Mat_<_Tp1, chs1>& _dst,
	const Mat_<_Tp2, chs2>& _xy, const Mat_<_Tp3, chs3>& _fxy, const void* _wtab, int borderType, const Scalar& _borderValue)
{
	typedef typename CastOp::rtype T;
	typedef typename CastOp::type1 WT;
	Size ssize = _src.size(), dsize = _dst.size();
	int k, cn = _src.channels;
	const AT* wtab = (const AT*)_wtab;
	const T* S0 = (const T*)_src.ptr();
	size_t sstep = _src.step / sizeof(S0[0]);
	T cval[CV_CN_MAX];
	int dx, dy;
	CastOp castOp;
 
	for (k = 0; k < cn; k++)
		cval[k] = saturate_cast<T>(_borderValue[k & 3]);
 
	unsigned width1 = std::max(ssize.width - 1, 0), height1 = std::max(ssize.height - 1, 0);
	FBC_Assert(ssize.area() > 0);
 
	for (dy = 0; dy < dsize.height; dy++) {
		T* D = (T*)_dst.ptr(dy);
		const short* XY = (const short*)_xy.ptr(dy);
		const ushort* FXY = (const ushort*)_fxy.ptr(dy);
		int X0 = 0;
		bool prevInlier = false;
 
		for (dx = 0; dx <= dsize.width; dx++) {
			bool curInlier = dx < dsize.width ? (unsigned)XY[dx * 2] < width1 && (unsigned)XY[dx * 2 + 1] < height1 : !prevInlier;
			if (curInlier == prevInlier)
				continue;
 
			int X1 = dx;
			dx = X0;
			X0 = X1;
			prevInlier = curInlier;
 
			if (!curInlier) {
				int len = 0;
				D += len*cn;
				dx += len;
 
				if (cn == 1) {
					for (; dx < X1; dx++, D++) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						const AT* w = wtab + FXY[dx] * 4;
						const T* S = S0 + sy*sstep + sx;
						*D = castOp(WT(S[0] * w[0] + S[1] * w[1] + S[sstep] * w[2] + S[sstep + 1] * w[3]));
					}
				} else if (cn == 2) {
					for (; dx < X1; dx++, D += 2) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						const AT* w = wtab + FXY[dx] * 4;
						const T* S = S0 + sy*sstep + sx * 2;
						WT t0 = S[0] * w[0] + S[2] * w[1] + S[sstep] * w[2] + S[sstep + 2] * w[3];
						WT t1 = S[1] * w[0] + S[3] * w[1] + S[sstep + 1] * w[2] + S[sstep + 3] * w[3];
						D[0] = castOp(t0); D[1] = castOp(t1);
					}
				} else if (cn == 3) {
					for (; dx < X1; dx++, D += 3) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						const AT* w = wtab + FXY[dx] * 4;
						const T* S = S0 + sy*sstep + sx * 3;
						WT t0 = S[0] * w[0] + S[3] * w[1] + S[sstep] * w[2] + S[sstep + 3] * w[3];
						WT t1 = S[1] * w[0] + S[4] * w[1] + S[sstep + 1] * w[2] + S[sstep + 4] * w[3];
						WT t2 = S[2] * w[0] + S[5] * w[1] + S[sstep + 2] * w[2] + S[sstep + 5] * w[3];
						D[0] = castOp(t0); D[1] = castOp(t1); D[2] = castOp(t2);
					}
				} else if (cn == 4) {
					for (; dx < X1; dx++, D += 4) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						const AT* w = wtab + FXY[dx] * 4;
						const T* S = S0 + sy*sstep + sx * 4;
						WT t0 = S[0] * w[0] + S[4] * w[1] + S[sstep] * w[2] + S[sstep + 4] * w[3];
						WT t1 = S[1] * w[0] + S[5] * w[1] + S[sstep + 1] * w[2] + S[sstep + 5] * w[3];
						D[0] = castOp(t0); D[1] = castOp(t1);
						t0 = S[2] * w[0] + S[6] * w[1] + S[sstep + 2] * w[2] + S[sstep + 6] * w[3];
						t1 = S[3] * w[0] + S[7] * w[1] + S[sstep + 3] * w[2] + S[sstep + 7] * w[3];
						D[2] = castOp(t0); D[3] = castOp(t1);
					}
				} else {
					for (; dx < X1; dx++, D += cn) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						const AT* w = wtab + FXY[dx] * 4;
						const T* S = S0 + sy*sstep + sx*cn;
						for (k = 0; k < cn; k++) {
							WT t0 = S[k] * w[0] + S[k + cn] * w[1] + S[sstep + k] * w[2] + S[sstep + k + cn] * w[3];
							D[k] = castOp(t0);
						}
					}
				}
			} else {
				if (borderType == BORDER_TRANSPARENT && cn != 3) {
					D += (X1 - dx)*cn;
					dx = X1;
					continue;
				}
 
				if (cn == 1) {
					for (; dx < X1; dx++, D++) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						//===BORDER_CONSTANT模式下,映射点超出dst范围,则跳过
						if (borderType == BORDER_CONSTANT && (sx >= ssize.width || sx + 1 < 0 || sy >= ssize.height || sy + 1 < 0)) {
							D[0] = cval[0];
						} else {
							int sx0, sx1, sy0, sy1;
							T v0, v1, v2, v3;
							const AT* w = wtab + FXY[dx] * 4;
							if (borderType == BORDER_REPLICATE) {
								sx0 = clip(sx, 0, ssize.width);
								sx1 = clip(sx + 1, 0, ssize.width);
								sy0 = clip(sy, 0, ssize.height);
								sy1 = clip(sy + 1, 0, ssize.height);
								v0 = S0[sy0*sstep + sx0];
								v1 = S0[sy0*sstep + sx1];
								v2 = S0[sy1*sstep + sx0];
								v3 = S0[sy1*sstep + sx1];
							} else {
								sx0 = borderInterpolate(sx, ssize.width, borderType);//===此BORDER_CONSTANT模式下,超出图像范围外,borderInterpolate返回-1
								sx1 = borderInterpolate(sx + 1, ssize.width, borderType);
								sy0 = borderInterpolate(sy, ssize.height, borderType);
								sy1 = borderInterpolate(sy + 1, ssize.height, borderType);
								v0 = sx0 >= 0 && sy0 >= 0 ? S0[sy0*sstep + sx0] : cval[0];
								v1 = sx1 >= 0 && sy0 >= 0 ? S0[sy0*sstep + sx1] : cval[0];
								v2 = sx0 >= 0 && sy1 >= 0 ? S0[sy1*sstep + sx0] : cval[0];
								v3 = sx1 >= 0 && sy1 >= 0 ? S0[sy1*sstep + sx1] : cval[0];
							}
							D[0] = castOp(WT(v0*w[0] + v1*w[1] + v2*w[2] + v3*w[3]));
						}
					}
				} else {
					for (; dx < X1; dx++, D += cn) {
						int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
						if (borderType == BORDER_CONSTANT && (sx >= ssize.width || sx + 1 < 0 || sy >= ssize.height || sy + 1 < 0)) {
							for (k = 0; k < cn; k++)
								D[k] = cval[k];
						} else {
							int sx0, sx1, sy0, sy1;
							const T *v0, *v1, *v2, *v3;
							const AT* w = wtab + FXY[dx] * 4;
							if (borderType == BORDER_REPLICATE) {
								sx0 = clip(sx, 0, ssize.width);
								sx1 = clip(sx + 1, 0, ssize.width);
								sy0 = clip(sy, 0, ssize.height);
								sy1 = clip(sy + 1, 0, ssize.height);
								v0 = S0 + sy0*sstep + sx0*cn;
								v1 = S0 + sy0*sstep + sx1*cn;
								v2 = S0 + sy1*sstep + sx0*cn;
								v3 = S0 + sy1*sstep + sx1*cn;
							} else if (borderType == BORDER_TRANSPARENT && ((unsigned)sx >= (unsigned)(ssize.width - 1) || (unsigned)sy >= (unsigned)(ssize.height - 1))) {
								continue;
							} else {
								sx0 = borderInterpolate(sx, ssize.width, borderType);
								sx1 = borderInterpolate(sx + 1, ssize.width, borderType);
								sy0 = borderInterpolate(sy, ssize.height, borderType);
								sy1 = borderInterpolate(sy + 1, ssize.height, borderType);
								v0 = sx0 >= 0 && sy0 >= 0 ? S0 + sy0*sstep + sx0*cn : &cval[0];
								v1 = sx1 >= 0 && sy0 >= 0 ? S0 + sy0*sstep + sx1*cn : &cval[0];
								v2 = sx0 >= 0 && sy1 >= 0 ? S0 + sy1*sstep + sx0*cn : &cval[0];
								v3 = sx1 >= 0 && sy1 >= 0 ? S0 + sy1*sstep + sx1*cn : &cval[0];
							}
							for (k = 0; k < cn; k++)
								D[k] = castOp(WT(v0[k] * w[0] + v1[k] * w[1] + v2[k] * w[2] + v3[k] * w[3]));
						}
					}
				}
			}
		}
	}
 
	return 0;
}
 
template<class CastOp, typename AT, int ONE, typename _Tp1, typename _Tp2, typename _Tp3, int chs1, int chs2, int chs3>
static int remapBicubic(const Mat_<_Tp1, chs1>& _src, Mat_<_Tp1, chs1>& _dst,
	const Mat_<_Tp2, chs2>& _xy, const Mat_<_Tp3, chs3>& _fxy, const void* _wtab, int borderType, const Scalar& _borderValue)
{
	typedef typename CastOp::rtype T;
	typedef typename CastOp::type1 WT;
	Size ssize = _src.size(), dsize = _dst.size();
	int cn = _src.channels;
	const AT* wtab = (const AT*)_wtab;
	const T* S0 = (const T*)_src.ptr();
	size_t sstep = _src.step / sizeof(S0[0]);
	Scalar_<T> cval(saturate_cast<T>(_borderValue[0]),
		saturate_cast<T>(_borderValue[1]),
		saturate_cast<T>(_borderValue[2]),
		saturate_cast<T>(_borderValue[3]));
	int dx, dy;
	CastOp castOp;
	int borderType1 = borderType != BORDER_TRANSPARENT ? borderType : BORDER_REFLECT_101;
 
	unsigned width1 = std::max(ssize.width - 3, 0), height1 = std::max(ssize.height - 3, 0);
 
	for (dy = 0; dy < dsize.height; dy++) {
		T* D = (T*)_dst.ptr(dy);
		const short* XY = (const short*)_xy.ptr(dy);
		const ushort* FXY = (const ushort*)_fxy.ptr(dy);
 
		for (dx = 0; dx < dsize.width; dx++, D += cn) {
			int sx = XY[dx * 2] - 1, sy = XY[dx * 2 + 1] - 1;
			const AT* w = wtab + FXY[dx] * 16;
			int i, k;
			if ((unsigned)sx < width1 && (unsigned)sy < height1) {
				const T* S = S0 + sy*sstep + sx*cn;
				for (k = 0; k < cn; k++) {
					WT sum = S[0] * w[0] + S[cn] * w[1] + S[cn * 2] * w[2] + S[cn * 3] * w[3];
					S += sstep;
					sum += S[0] * w[4] + S[cn] * w[5] + S[cn * 2] * w[6] + S[cn * 3] * w[7];
					S += sstep;
					sum += S[0] * w[8] + S[cn] * w[9] + S[cn * 2] * w[10] + S[cn * 3] * w[11];
					S += sstep;
					sum += S[0] * w[12] + S[cn] * w[13] + S[cn * 2] * w[14] + S[cn * 3] * w[15];
					S += 1 - sstep * 3;
					D[k] = castOp(sum);
				}
			} else {
				int x[4], y[4];
				if (borderType == BORDER_TRANSPARENT &&
					((unsigned)(sx + 1) >= (unsigned)ssize.width ||
					(unsigned)(sy + 1) >= (unsigned)ssize.height))
					continue;
 
				if (borderType1 == BORDER_CONSTANT &&
					(sx >= ssize.width || sx + 4 <= 0 ||
					sy >= ssize.height || sy + 4 <= 0)) {
					for (k = 0; k < cn; k++)
						D[k] = cval[k];
					continue;
				}
 
				for (i = 0; i < 4; i++) {
					x[i] = borderInterpolate(sx + i, ssize.width, borderType1)*cn;
					y[i] = borderInterpolate(sy + i, ssize.height, borderType1);
				}
 
				for (k = 0; k < cn; k++, S0++, w -= 16) {
					WT cv = cval[k], sum = cv*ONE;
					for (i = 0; i < 4; i++, w += 4) {
						int yi = y[i];
						const T* S = S0 + yi*sstep;
						if (yi < 0)
							continue;
						if (x[0] >= 0)
							sum += (S[x[0]] - cv)*w[0];
						if (x[1] >= 0)
							sum += (S[x[1]] - cv)*w[1];
						if (x[2] >= 0)
							sum += (S[x[2]] - cv)*w[2];
						if (x[3] >= 0)
							sum += (S[x[3]] - cv)*w[3];
					}
					D[k] = castOp(sum);
				}
				S0 -= cn;
			}
		}
	}
 
	return 0;
}
 
template<class CastOp, typename AT, int ONE, typename _Tp1, typename _Tp2, typename _Tp3, int chs1, int chs2, int chs3>
static int remapLanczos4(const Mat_<_Tp1, chs1>& _src, Mat_<_Tp1, chs1>& _dst,
	const Mat_<_Tp2, chs2>& _xy, const Mat_<_Tp3, chs3>& _fxy, const void* _wtab, int borderType, const Scalar& _borderValue)
{
	typedef typename CastOp::rtype T;
	typedef typename CastOp::type1 WT;
	Size ssize = _src.size(), dsize = _dst.size();
	int cn = _src.channels;
	const AT* wtab = (const AT*)_wtab;
	const T* S0 = (const T*)_src.ptr();
	size_t sstep = _src.step / sizeof(S0[0]);
	Scalar_<T> cval(saturate_cast<T>(_borderValue[0]),
		saturate_cast<T>(_borderValue[1]),
		saturate_cast<T>(_borderValue[2]),
		saturate_cast<T>(_borderValue[3]));
	int dx, dy;
	CastOp castOp;
	int borderType1 = borderType != BORDER_TRANSPARENT ? borderType : BORDER_REFLECT_101;
 
	unsigned width1 = std::max(ssize.width - 7, 0), height1 = std::max(ssize.height - 7, 0);
 
	for (dy = 0; dy < dsize.height; dy++) {
		T* D = (T*)_dst.ptr(dy);
		const short* XY = (const short*)_xy.ptr(dy);
		const ushort* FXY = (const ushort*)_fxy.ptr(dy);
 
		for (dx = 0; dx < dsize.width; dx++, D += cn) {
			int sx = XY[dx * 2] - 3, sy = XY[dx * 2 + 1] - 3;
			const AT* w = wtab + FXY[dx] * 64;
			const T* S = S0 + sy*sstep + sx*cn;
			int i, k;
			if ((unsigned)sx < width1 && (unsigned)sy < height1) {
				for (k = 0; k < cn; k++) {
					WT sum = 0;
					for (int r = 0; r < 8; r++, S += sstep, w += 8)
						sum += S[0] * w[0] + S[cn] * w[1] + S[cn * 2] * w[2] + S[cn * 3] * w[3] +
						S[cn * 4] * w[4] + S[cn * 5] * w[5] + S[cn * 6] * w[6] + S[cn * 7] * w[7];
					w -= 64;
					S -= sstep * 8 - 1;
					D[k] = castOp(sum);
				}
			} else {
				int x[8], y[8];
				if (borderType == BORDER_TRANSPARENT &&
					((unsigned)(sx + 3) >= (unsigned)ssize.width ||
					(unsigned)(sy + 3) >= (unsigned)ssize.height))
					continue;
 
				if (borderType1 == BORDER_CONSTANT &&
					(sx >= ssize.width || sx + 8 <= 0 ||
					sy >= ssize.height || sy + 8 <= 0)) {
					for (k = 0; k < cn; k++)
						D[k] = cval[k];
					continue;
				}
 
				for (i = 0; i < 8; i++) {
					x[i] = borderInterpolate(sx + i, ssize.width, borderType1)*cn;
					y[i] = borderInterpolate(sy + i, ssize.height, borderType1);
				}
 
				for (k = 0; k < cn; k++, S0++, w -= 64) {
					WT cv = cval[k], sum = cv*ONE;
					for (i = 0; i < 8; i++, w += 8) {
						int yi = y[i];
						const T* S1 = S0 + yi*sstep;
						if (yi < 0)
							continue;
						if (x[0] >= 0)
							sum += (S1[x[0]] - cv)*w[0];
						if (x[1] >= 0)
							sum += (S1[x[1]] - cv)*w[1];
						if (x[2] >= 0)
							sum += (S1[x[2]] - cv)*w[2];
						if (x[3] >= 0)
							sum += (S1[x[3]] - cv)*w[3];
						if (x[4] >= 0)
							sum += (S1[x[4]] - cv)*w[4];
						if (x[5] >= 0)
							sum += (S1[x[5]] - cv)*w[5];
						if (x[6] >= 0)
							sum += (S1[x[6]] - cv)*w[6];
						if (x[7] >= 0)
							sum += (S1[x[7]] - cv)*w[7];
					}
					D[k] = castOp(sum);
				}
				S0 -= cn;
			}
		}
	}
 
	return 0;
}
 
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
static int remap_nearest(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue)
{
	const void* ctab = 0;
	bool fixpt = typeid(uchar).name() == typeid(_Tp1).name();
	bool planar_input = map1.channels == 1;
	Range range(0, dst.rows);
 
	int x, y, x1, y1;
	const int buf_size = 1 << 14;
	int brows0 = std::min(128, dst.rows);
	int bcols0 = std::min(buf_size / brows0, dst.cols);
	brows0 = std::min(buf_size / bcols0, dst.rows);
 
	Mat_<short, 2> _bufxy(brows0, bcols0);
 
	for (y = range.start; y < range.end; y += brows0) {
		for (x = 0; x < dst.cols; x += bcols0) {
			int brows = std::min(brows0, range.end - y);
			int bcols = std::min(bcols0, dst.cols - x);
			Mat_<_Tp1, chs1> dpart;
			dst.getROI(dpart, Rect(x, y, bcols, brows));
			Mat_<short, 2> bufxy;
			_bufxy.getROI(bufxy, Rect(0, 0, bcols, brows));
 
			if (sizeof(_Tp2) == sizeof(short)) { // short
				for (y1 = 0; y1 < brows; y1++) {
					short* XY = (short*)bufxy.ptr(y1);
					const short* sXY = (const short*)map1.ptr(y + y1) + x * 2;
					const ushort* sA = (const ushort*)map2.ptr(y + y1) + x;
 
					for (x1 = 0; x1 < bcols; x1++) {
						int a = sA[x1] & (INTER_TAB_SIZE2 - 1);
						XY[x1 * 2] = sXY[x1 * 2] + NNDeltaTab_i[a][0];
						XY[x1 * 2 + 1] = sXY[x1 * 2 + 1] + NNDeltaTab_i[a][1];
					}
				}
			} else { // float
				for (y1 = 0; y1 < brows; y1++) {
					short* XY = (short*)bufxy.ptr(y1);
					const float* sX = (const float*)map1.ptr(y + y1) + x;
					const float* sY = (const float*)map2.ptr(y + y1) + x;
 
					x1 = 0;
					for (; x1 < bcols; x1++) {
						XY[x1 * 2] = saturate_cast<short>(sX[x1]);
						XY[x1 * 2 + 1] = saturate_cast<short>(sY[x1]);
					}
				}
			}
 
			remapNearest<_Tp1, short, chs1, 2>(src, dpart, bufxy, borderMode, borderValue);
		}
	}
 
	return 0;
}
 
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
static int remap_linear(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue)
{
	const void* ctab = 0;
	bool fixpt = typeid(uchar).name() == typeid(_Tp1).name();
	bool planar_input = map1.channels == 1;
	ctab = initInterTab2D<_Tp1>(INTER_LINEAR, fixpt);
	Range range(0, dst.rows);
 
	int x, y, x1, y1;
	const int buf_size = 1 << 14;
	int brows0 = std::min(128, dst.rows);
	int bcols0 = std::min(buf_size / brows0, dst.cols);
	brows0 = std::min(buf_size / bcols0, dst.rows);
 
	Mat_<short, 2> _bufxy(brows0, bcols0);
	Mat_<ushort, 1> _bufa(brows0, bcols0);
 
	for (y = range.start; y < range.end; y += brows0) {
		for (x = 0; x < dst.cols; x += bcols0) {
			int brows = std::min(brows0, range.end - y);
			int bcols = std::min(bcols0, dst.cols - x);
			Mat_<_Tp1, chs1> dpart;
			dst.getROI(dpart, Rect(x, y, bcols, brows));
			Mat_<short, 2> bufxy;
			_bufxy.getROI(bufxy, Rect(0, 0, bcols, brows));
			Mat_<ushort, 1> bufa;
			_bufa.getROI(bufa, Rect(0, 0, bcols, brows));
 
			for (y1 = 0; y1 < brows; y1++) {
				short* XY = (short*)bufxy.ptr(y1);
				ushort* A = (ushort*)bufa.ptr(y1);
 
				if (planar_input) {
					const float* sX = (const float*)map1.ptr(y + y1) + x;
					const float* sY = (const float*)map2.ptr(y + y1) + x;
 
					x1 = 0;
					for (; x1 < bcols; x1++) {
						int sx = fbcRound(sX[x1] * INTER_TAB_SIZE);
						int sy = fbcRound(sY[x1] * INTER_TAB_SIZE);
						int v = (sy & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE - 1));
						XY[x1 * 2] = saturate_cast<short>(sx >> INTER_BITS);
						XY[x1 * 2 + 1] = saturate_cast<short>(sy >> INTER_BITS);
						A[x1] = (ushort)v;
					}
				} else {
					const float* sXY = (const float*)map1.ptr(y + y1) + x * 2;
					x1 = 0;
					for (x1 = 0; x1 < bcols; x1++) {
						int sx = fbcRound(sXY[x1 * 2] * INTER_TAB_SIZE);
						int sy = fbcRound(sXY[x1 * 2 + 1] * INTER_TAB_SIZE);
						int v = (sy & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE - 1));
						XY[x1 * 2] = saturate_cast<short>(sx >> INTER_BITS);
						XY[x1 * 2 + 1] = saturate_cast<short>(sy >> INTER_BITS);
						A[x1] = (ushort)v;
					}
				}
			}
 
			if (typeid(_Tp1).name() == typeid(uchar).name()) { // uchar
				remapBilinear<FixedPtCast<int, uchar, INTER_REMAP_COEF_BITS>, short, _Tp1, short, ushort, chs1, 2, 1>(src, dpart, bufxy, bufa, ctab, borderMode, borderValue);
			} else { // float
				remapBilinear<Cast<float, float>, float, _Tp1, short, ushort, chs1, 2, 1>(src, dpart, bufxy, bufa, ctab, borderMode, borderValue);
			}
		}
	}
 
	return 0;
}
 
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
static int remap_cubic(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue)
{
	const void* ctab = 0;
	bool fixpt = typeid(uchar).name() == typeid(_Tp1).name();
	bool planar_input = map1.channels == 1;
	ctab = initInterTab2D<_Tp1>(INTER_CUBIC, fixpt);
	Range range(0, dst.rows);
 
	int x, y, x1, y1;
	const int buf_size = 1 << 14;
	int brows0 = std::min(128, dst.rows);
	int bcols0 = std::min(buf_size / brows0, dst.cols);
	brows0 = std::min(buf_size / bcols0, dst.rows);
 
	Mat_<short, 2> _bufxy(brows0, bcols0);
	Mat_<ushort, 1> _bufa(brows0, bcols0);
 
	for (y = range.start; y < range.end; y += brows0) {
		for (x = 0; x < dst.cols; x += bcols0) {
			int brows = std::min(brows0, range.end - y);
			int bcols = std::min(bcols0, dst.cols - x);
			Mat_<_Tp1, chs1> dpart;
			dst.getROI(dpart, Rect(x, y, bcols, brows));
			Mat_<short, 2> bufxy;
			_bufxy.getROI(bufxy, Rect(0, 0, bcols, brows));
			Mat_<ushort, 1> bufa;
			_bufa.getROI(bufa, Rect(0, 0, bcols, brows));
 
			for (y1 = 0; y1 < brows; y1++) {
				short* XY = (short*)bufxy.ptr(y1);
				ushort* A = (ushort*)bufa.ptr(y1);
 
				if (planar_input) {
					const float* sX = (const float*)map1.ptr(y + y1) + x;
					const float* sY = (const float*)map2.ptr(y + y1) + x;
 
					x1 = 0;
					for (; x1 < bcols; x1++) {
						int sx = fbcRound(sX[x1] * INTER_TAB_SIZE);
						int sy = fbcRound(sY[x1] * INTER_TAB_SIZE);
						int v = (sy & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE - 1));
						XY[x1 * 2] = saturate_cast<short>(sx >> INTER_BITS);
						XY[x1 * 2 + 1] = saturate_cast<short>(sy >> INTER_BITS);
						A[x1] = (ushort)v;
					}
				} else {
					const float* sXY = (const float*)map1.ptr(y + y1) + x * 2;
					x1 = 0;
					for (x1 = 0; x1 < bcols; x1++) {
						int sx = fbcRound(sXY[x1 * 2] * INTER_TAB_SIZE);
						int sy = fbcRound(sXY[x1 * 2 + 1] * INTER_TAB_SIZE);
						int v = (sy & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE - 1));
						XY[x1 * 2] = saturate_cast<short>(sx >> INTER_BITS);
						XY[x1 * 2 + 1] = saturate_cast<short>(sy >> INTER_BITS);
						A[x1] = (ushort)v;
					}
				}
			}
 
			if (typeid(_Tp1).name() == typeid(uchar).name()) { // uchar
				remapBicubic<FixedPtCast<int, uchar, INTER_REMAP_COEF_BITS>, short, INTER_REMAP_COEF_SCALE, _Tp1, short, ushort, chs1, 2, 1>(src, dpart, bufxy, bufa, ctab, borderMode, borderValue);
			} else { // float
				remapBicubic<Cast<float, float>, float, 1, _Tp1, short, ushort, chs1, 2, 1>(src, dpart, bufxy, bufa, ctab, borderMode, borderValue);
			}
		}
	}
 
	return 0;
}
 
template<typename _Tp1, typename _Tp2, int chs1, int chs2>
static int remap_lanczos4(const Mat_<_Tp1, chs1>& src, Mat_<_Tp1, chs1>& dst,
	const Mat_<_Tp2, chs2>& map1, const Mat_<_Tp2, chs2>& map2, int borderMode, const Scalar& borderValue)
{
	const void* ctab = 0;
	bool fixpt = typeid(uchar).name() == typeid(_Tp1).name();
	bool planar_input = map1.channels == 1;
	ctab = initInterTab2D<_Tp1>(INTER_LANCZOS4, fixpt);
	Range range(0, dst.rows);
 
	int x, y, x1, y1;
	const int buf_size = 1 << 14;
	int brows0 = std::min(128, dst.rows);
	int bcols0 = std::min(buf_size / brows0, dst.cols);
	brows0 = std::min(buf_size / bcols0, dst.rows);
 
	Mat_<short, 2> _bufxy(brows0, bcols0);
	Mat_<ushort, 1> _bufa(brows0, bcols0);
 
	for (y = range.start; y < range.end; y += brows0) {
		for (x = 0; x < dst.cols; x += bcols0) {
			int brows = std::min(brows0, range.end - y);
			int bcols = std::min(bcols0, dst.cols - x);
			Mat_<_Tp1, chs1> dpart;
			dst.getROI(dpart, Rect(x, y, bcols, brows));
			Mat_<short, 2> bufxy;
			_bufxy.getROI(bufxy, Rect(0, 0, bcols, brows));
			Mat_<ushort, 1> bufa;
			_bufa.getROI(bufa, Rect(0, 0, bcols, brows));
 
			for (y1 = 0; y1 < brows; y1++) {
				short* XY = (short*)bufxy.ptr(y1);
				ushort* A = (ushort*)bufa.ptr(y1);
 
				if (planar_input) {
					const float* sX = (const float*)map1.ptr(y + y1) + x;
					const float* sY = (const float*)map2.ptr(y + y1) + x;
 
					x1 = 0;
					for (; x1 < bcols; x1++) {
						int sx = fbcRound(sX[x1] * INTER_TAB_SIZE);
						int sy = fbcRound(sY[x1] * INTER_TAB_SIZE);
						int v = (sy & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE - 1));
						XY[x1 * 2] = saturate_cast<short>(sx >> INTER_BITS);
						XY[x1 * 2 + 1] = saturate_cast<short>(sy >> INTER_BITS);
						A[x1] = (ushort)v;
					}
				} else {
					const float* sXY = (const float*)map1.ptr(y + y1) + x * 2;
					x1 = 0;
					for (x1 = 0; x1 < bcols; x1++) {
						int sx = fbcRound(sXY[x1 * 2] * INTER_TAB_SIZE);
						int sy = fbcRound(sXY[x1 * 2 + 1] * INTER_TAB_SIZE);
						int v = (sy & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE - 1));
						XY[x1 * 2] = saturate_cast<short>(sx >> INTER_BITS);
						XY[x1 * 2 + 1] = saturate_cast<short>(sy >> INTER_BITS);
						A[x1] = (ushort)v;
					}
				}
			}
 
			if (typeid(_Tp1).name() == typeid(uchar).name()) { // uchar
				remapLanczos4<FixedPtCast<int, uchar, INTER_REMAP_COEF_BITS>, short, INTER_REMAP_COEF_SCALE, _Tp1, short, ushort, chs1, 2, 1>(src, dpart, bufxy, bufa, ctab, borderMode, borderValue);
			}
			else { // float
				remapLanczos4<Cast<float, float>, float, 1, _Tp1, short, ushort, chs1, 2, 1>(src, dpart, bufxy, bufa, ctab, borderMode, borderValue);
			}
		}
	}
 
	return 0;
}
 
} // namespace fbc
 
#endif // FBC_CV_REMAP_HPP_

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值