OpenCV代码提取:remap函数的实现

图像重映射:即是把一个图像中一个位置的像素放置到另一个图像指定位置的过程。因为有时候源图像和目标图像并不总是一一对应的关系(目标图像的像素可能映射到源图像的非整数坐标上),所以有时候会需要做插值。

         描述映射过程:通过重映射来表达每个像素的位置(x,y):g(x,y) = f(h(x,y))

         其中:g()是目标图像,即重映射的结果;f()是源图像;h(x,y)是作用于(x,y)的映射方法函数。如h(x,y)=(I.cols-x,y),得到的目标图像将是按照x轴方向发生翻转。

         映射分为向前映射和向后映射。将输入映射到输出的向前映射,反正就是向后映射。即如果已知源图像到目标图像的坐标变换,即可以知道源图像的一点在变换后在目标图像的位置,称为向前映射。如果知道目标图像的一点在变换前在源图像上的位置,称为向后映射。向后映射比较直观,计算量小,OpenCV中经常使用的图像变换都是采用向后映射的方法来处理。但向后映射需要知道变换的反变换公式,但在有些变换比较复杂的场合,这个反变换是很难得到的。此时就需要采用前向映射的方法进行变换了。

这里对OpenCV中remap函数进行了提取目前支持uchar和float两种类型,经测试,与OpenCV3.1结果完全一致。

实现代码remap.hpp:

// 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];
						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);
								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_
测试代码test_remap.cpp:

#include "test_remap.hpp"
#include <assert.h>
#include <opencv2/opencv.hpp>
#include <remap.hpp>

static void update_map(const cv::Mat& src, cv::Mat& map_x, cv::Mat& map_y, int ind_)
{
	int ind = ind_ % 4;

	for (int j = 0; j < src.rows; j++) {
		for (int i = 0; i < src.cols; i++) {
			switch (ind) {
				case 0:
					if (i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75) {
						map_x.at<float>(j, i) = 2 * (i - src.cols*0.25) + 0.5;
						map_y.at<float>(j, i) = 2 * (j - src.rows*0.25) + 0.5;
					} else {
						map_x.at<float>(j, i) = 0;
						map_y.at<float>(j, i) = 0;
					}
					break;
				case 1:
					map_x.at<float>(j, i) = i;
					map_y.at<float>(j, i) = src.rows - j;
					break;
				case 2:
					map_x.at<float>(j, i) = src.cols - i;
					map_y.at<float>(j, i) = j;
					break;
				case 3:
					map_x.at<float>(j, i) = src.cols - i;
					map_y.at<float>(j, i) = src.rows - j;
					break;
			} // end of switch
		}
	}
}

int test_remap_uchar()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}

	for (int ind = 0; ind < 4; ind++) {
		for (int interpolation = 0; interpolation < 5; interpolation++) {
			for (int borderType = 0; borderType < 6; borderType++) {
				cv::Mat map_x, map_y;
				map_x.create(matSrc.size(), CV_32FC1);
				map_y.create(matSrc.size(), CV_32FC1);

				update_map(matSrc, map_x, map_y, ind);

				int width = matSrc.cols;
				int height = matSrc.rows;

				fbc::Mat_<fbc::uchar, 3> mat1(height, width, matSrc.data);
				fbc::Mat_<float, 1> mapX(height, width, map_x.data);
				fbc::Mat_<float, 1> mapY(height, width, map_y.data);
				fbc::Mat_<fbc::uchar, 3> mat2(height, width);

				fbc::remap(mat1, mat2, mapX, mapY, interpolation, borderType, fbc::Scalar::all(0));

				cv::Mat mat2_ = cv::Mat(height, width, CV_8UC3);
				cv::remap(matSrc, mat2_, map_x, map_y, interpolation, borderType, cv::Scalar::all(0));

				assert(mat2.step == mat2_.step);
				for (int y = 0; y < mat2.rows; y++) {
					const fbc::uchar* p = mat2.ptr(y);
					const uchar* p_ = mat2_.ptr(y);

					for (int x = 0; x < mat2.step; x++) {
						assert(p[x] == p_[x]);
					}
				}
			}
		}
	}

	return 0;
}

int test_remap_float()
{
	cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
	if (!matSrc.data) {
		std::cout << "read image fail" << std::endl;
		return -1;
	}
	matSrc.convertTo(matSrc, CV_32FC3);

	for (int ind = 0; ind < 4; ind++) {
		for (int interpolation = 0; interpolation < 5; interpolation++) {
			for (int borderType = 0; borderType < 6; borderType++) {
				cv::Mat map_x, map_y;
				map_x.create(matSrc.size(), CV_32FC1);
				map_y.create(matSrc.size(), CV_32FC1);

				update_map(matSrc, map_x, map_y, ind);

				int width = matSrc.cols;
				int height = matSrc.rows;

				fbc::Mat_<float, 3> mat1(height, width, matSrc.data);
				fbc::Mat_<float, 1> mapX(height, width, map_x.data);
				fbc::Mat_<float, 1> mapY(height, width, map_y.data);
				fbc::Mat_<float, 3> mat2(height, width);

				fbc::remap(mat1, mat2, mapX, mapY, interpolation, borderType, fbc::Scalar::all(0));

				cv::Mat mat2_ = cv::Mat(height, width, CV_32FC3);
				cv::remap(matSrc, mat2_, map_x, map_y, interpolation, borderType, cv::Scalar::all(0));

				assert(mat2.step == mat2_.step);
				for (int y = 0; y < mat2.rows; y++) {
					const fbc::uchar* p = mat2.ptr(y);
					const uchar* p_ = mat2_.ptr(y);

					for (int x = 0; x < mat2.step; x++) {
						assert(p[x] == p_[x]);
					}
				}
			}
		}
	}

	return 0;
}

GitHubhttps://github.com/fengbingchun/OpenCV_Test

  • 8
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
cv::remap()函数OpenCV的一个图像处理函数,用于对图像进行重映射。重映射是一种常见的图像处理操作,它可以将一个图像的像素按照一定的规则映射到另一个图像的像素位置上。 具体来说,cv::remap()函数可以将一个输入图像的像素根据指定的映射关系,映射到输出图像的指定位置上。这个映射关系是通过两个输入参数map1和map2来指定的,这两个参数分别是存储了x和y方向上的映射关系的两个矩阵。矩阵的每个元素对应于输出图像的一个像素位置,其值表示了输入图像对应像素的位置。 cv::remap()函数的具体用法如下: ```cpp void cv::remap( cv::InputArray src, // 输入图像 cv::OutputArray dst, // 输出图像 cv::InputArray map1, // x方向上的映射关系 cv::InputArray map2, // y方向上的映射关系 int interpolation, // 插值方式 int borderMode = cv::BORDER_CONSTANT, // 边缘处理方式 const cv::Scalar& borderValue = cv::Scalar() // 边缘填充值 ); ``` 其,interpolation参数表示了插值方式,常用的插值方式有: - cv::INTER_NEAREST:最近邻插值法 - cv::INTER_LINEAR:双线性插值法 - cv::INTER_CUBIC:双三次插值法 - cv::INTER_LANCZOS4:Lanczos插值法 borderMode参数表示了边缘处理方式,常用的边缘处理方式有: - cv::BORDER_CONSTANT:常数边缘填充 - cv::BORDER_REPLICATE:复制边缘像素 - cv::BORDER_REFLECT:反射边缘像素 - cv::BORDER_WRAP:环绕边缘处理 borderValue参数表示了边缘填充值,如果使用常数边缘填充方式,则需要指定该值。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值