图像重映射:即是把一个图像中一个位置的像素放置到另一个图像指定位置的过程。因为有时候源图像和目标图像并不总是一一对应的关系(目标图像的像素可能映射到源图像的非整数坐标上),所以有时候会需要做插值。
描述映射过程:通过重映射来表达每个像素的位置(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: [email protected]
#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;
}
GitHub: https://github.com/fengbingchun/OpenCV_Test
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow