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, 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; //===&#x8FD0;&#x884C;&#x4F18;&#x5316;&#x6240;&#x9700;&#xFF0C;&#x5212;&#x5206;&#x6210;&#x5C0F;&#x5757;&#x5904;&#x7406;
    short XY[BLOCK_SZ*BLOCK_SZ * 2], A[BLOCK_SZ*BLOCK_SZ];//=== XY dst&#x6BCF;&#x4E2A;&#x50CF;&#x7D20;&#x5BF9;&#x5E94;src&#x4E0A;&#x7684;&#x5750;&#x6807;&#xFF1B;A&#x5728;&#x975E;&#x6700;&#x90BB;&#x8FD1;&#x5DEE;&#x503C;&#x6A21;&#x5F0F;&#x4E0B;&#x8D77;&#x4F5C;&#x7528;&#xFF0C;&#x4E3A;&#x8FD0;&#x884C;&#x4F18;&#x5316;&#x5904;&#x7406;&#x5757;&#x4E2D;&#x7684;&#x4F4D;&#x7F6E;&#x7D22;&#x5F15;
    int x, y, x1, y1, width = dst.cols, height = dst.rows;

    int bh0 = std::min(BLOCK_SZ / 2, height);//=== &#x6B63;&#x5E38;&#x60C5;&#x51B5;&#xFF0C;XY&#x5BF9;&#x5E94;&#x7684;&#x50CF;&#x7D20;&#x5757;&#x662F;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&#x9632;&#x6B62;&#x6EA2;&#x51FA;
                        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&#x6BCF;&#x4E2A;&#x50CF;&#x7D20;&#x5BF9;&#x5E94;src&#x4E0A;&#x7684;&#x5750;&#x6807;-x
                        xy[x1 * 2 + 1] = saturate_cast<short>(Y >> INTER_BITS);//===dst&#x6BCF;&#x4E2A;&#x50CF;&#x7D20;&#x5BF9;&#x5E94;src&#x4E0A;&#x7684;&#x5750;&#x6807;-y
                        alpha[x1] = (short)((Y & (INTER_TAB_SIZE - 1))*INTER_TAB_SIZE + (X & (INTER_TAB_SIZE - 1)));//===&#x5728;&#x975E;&#x6700;&#x90BB;&#x8FD1;&#x5DEE;&#x503C;&#x6A21;&#x5F0F;&#x4E0B;&#x8D77;&#x4F5C;&#x7528;&#xFF0C;&#x4E3A;&#x8FD0;&#x884C;&#x4F18;&#x5316;&#x5904;&#x7406;&#x5757;&#x4E2D;&#x7684;&#x4F4D;&#x7F6E;&#x7D22;&#x5F15;
                    }
                }
            }

            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_</ushort,></float,></short></short></int></int></short></short></int></int></_tp1,></short,></double,></_tp2,></_tp1,></_tp1,></typename></double,></typeinfo>

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 指定的颜色。
// &#x4EE3;&#x7801;&#x6E90;&#x94FE;&#x63A5;&#xFF1A;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, 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, 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, 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, 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, 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, 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, _tp1, _tp2, _tp3, int chs1, chs2, 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 <= 2 dsize.width; dx++) { bool curinlier="dx" < dsize.width ? (unsigned)xy[dx * 2] width1 && + 1] height1 : !previnlier; if (curinlier="=" previnlier) continue; int x1="dx;" dx="X0;" x0="X1;" previnlier="curInlier;" (!curinlier) len="0;" d (cn="=" 1) for (; x1; dx++, d++) sx="XY[dx" 2], sy="XY[dx" 1]; const at* w="wtab" fxy[dx] 4; t* s="S0" sy*sstep sx; *d="castOp(WT(S[0]" w[0] s[1] w[1] s[sstep] w[2] s[sstep w[3])); } else 2) 2; wt t0="S[0]" s[2] w[3]; t1="S[1]" s[3] 3] d[0]="castOp(t0);" d[1]="castOp(t1);" 3) 3; s[4] 4] t2="S[2]" s[5] 5] d[2]="castOp(t2);" 4) s[6] 6] s[7] 7] d[3]="castOp(t1);" sx*cn; (k="0;" k cn; k++) s[k cn] k] d[k]="castOp(t0);" (bordertype="=" border_transparent cn !="3)" - dx)*cn; =="=BORDER_CONSTANT&#x6A21;&#x5F0F;&#x4E0B;&#xFF0C;&#x6620;&#x5C04;&#x70B9;&#x8D85;&#x51FA;dst&#x8303;&#x56F4;&#xFF0C;&#x5219;&#x8DF3;&#x8FC7;" 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);//===&#x6B64;BORDER_CONSTANT&#x6A21;&#x5F0F;&#x4E0B;&#xFF0C;&#x8D85;&#x51FA;&#x56FE;&#x50CF;&#x8303;&#x56F4;&#x5916;&#xFF0C;borderInterpolate&#x8FD4;&#x56DE;-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, _tp1, _tp2, _tp3, chs1, chs2, 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; } (i="0;" i 4; i++) x[i]="borderInterpolate(sx" + i, ssize.width, bordertype1)*cn; y[i]="borderInterpolate(sy" ssize.height, bordertype1); k++, s0++, w -="16)" wt cv="cval[k]," sum="cv*ONE;" i++, int yi="y[i];" const t* s="S0" yi*sstep; if (yi 0) (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, _tp1, _tp2, _tp3, chs1, chs2, 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; } (i="0;" i 8; i++) x[i]="borderInterpolate(sx" + i, ssize.width, bordertype1)*cn; y[i]="borderInterpolate(sy" ssize.height, bordertype1); k++, s0++, w -="64)" wt cv="cval[k]," sum="cv*ONE;" i++, int yi="y[i];" const t* s1="S0" yi*sstep; if (yi 0) (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, 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, 2 short, chs1,>(src, dpart, bufxy, borderMode, borderValue);
        }
    }

    return 0;
}

template<typename _tp1, typename _tp2, int chs1, 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, 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, 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_</cast<float,></fixedptcast<int,></short></short></short></short></ushort,></short,></_tp1,></ushort,></short,></_tp1></_tp2,></_tp2,></_tp1,></_tp1,></typename></cast<float,></fixedptcast<int,></short></short></short></short></ushort,></short,></_tp1,></ushort,></short,></_tp1></_tp2,></_tp2,></_tp1,></_tp1,></typename></cast<float,></fixedptcast<int,></short></short></short></short></ushort,></short,></_tp1,></ushort,></short,></_tp1></_tp2,></_tp2,></_tp1,></_tp1,></typename></_tp1,></short></short></short,></_tp1,></short,></_tp2,></_tp2,></_tp1,></_tp1,></typename></=></=></t></t></t></t></t></_tp3,></_tp2,></_tp1,></_tp1,></class></=></=></t></t></t></t></t></_tp3,></_tp2,></_tp1,></_tp1,></class></=></t></_tp3,></_tp2,></_tp1,></_tp1,></class></int></int></int></int></_tp1></_tp1></_tp1></_tp1></_tp1></_tp2,></_tp1,></_tp1,></typename></uchar></uchar></uchar></uchar></uchar></uchar></uchar></typename></short></float></float></typename></float></float></float></typename></typename></_tp2,></_tp2,></_tp1,></_tp1,></typename></_tp2,></_tp2,></_tp1,></_tp1,></typename></_tp2,></_tp2,></_tp1,></_tp1,></typename></_tp2,></_tp2,></_tp1,></_tp1,></typename></_tp2,></_tp2,></_tp1,></_tp1,></typename></typeinfo>

Original: https://blog.csdn.net/qitanhl/article/details/124251191
Author: qitanhl
Title: opencv 仿射变换代码解析 warpPerspective(),remap()

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/704394/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球