void cv::remap( InputArray _src, OutputArray _dst, InputArray _map1, InputArray _map2, int interpolation, int borderType, const Scalar& borderValue ) { static RemapNNFunc nn_tab[] = { remapNearest, remapNearest, remapNearest, remapNearest, remapNearest, remapNearest, remapNearest, 0 }; static RemapFunc linear_tab[] = { remapBilinear, RemapVec_8u, short>, 0, remapBilinear, RemapNoVec, float>, remapBilinear, RemapNoVec, float>, 0, remapBilinear, RemapNoVec, float>, remapBilinear, RemapNoVec, float>, 0 }; static RemapFunc cubic_tab[] = { remapBicubic, short, INTER_REMAP_COEF_SCALE>, 0, remapBicubic, float, 1>, remapBicubic, float, 1>, 0, remapBicubic, float, 1>, remapBicubic, float, 1>, 0 }; static RemapFunc lanczos4_tab[] = { remapLanczos4, short, INTER_REMAP_COEF_SCALE>, 0, remapLanczos4, float, 1>, remapLanczos4, float, 1>, 0, remapLanczos4, float, 1>, remapLanczos4, float, 1>, 0 }; Mat src = _src.getMat(), map1 = _map1.getMat(), map2 = _map2.getMat(); CV_Assert( (!map2.data || map2.size() == map1.size())); _dst.create( map1.size(), src.type() ); Mat dst = _dst.getMat(); CV_Assert(dst.data != src.data); int depth = src.depth(), map_depth = map1.depth(); RemapNNFunc nnfunc = 0; RemapFunc ifunc = 0; const void* ctab = 0; bool fixpt = depth == CV_8U; bool planar_input = false; if( interpolation == INTER_NEAREST ) { nnfunc = nn_tab[depth]; CV_Assert( nnfunc != 0 ); if( map1.type() == CV_16SC2 && !map2.data ) // the data is already in the right format { nnfunc( src, dst, map1, borderType, borderValue ); return; } } else { if( interpolation == INTER_AREA ) interpolation = INTER_LINEAR; if( interpolation == INTER_LINEAR ) ifunc = linear_tab[depth]; else if( interpolation == INTER_CUBIC ) ifunc = cubic_tab[depth]; else if( interpolation == INTER_LANCZOS4 ) ifunc = lanczos4_tab[depth]; else CV_Error( CV_StsBadArg, "Unknown interpolation method" ); CV_Assert( ifunc != 0 ); ctab = initInterTab2D( interpolation, fixpt ); } const Mat *m1 = &map1, *m2 = &map2; if( (map1.type() == CV_16SC2 && (map2.type() == CV_16UC1 || map2.type() == CV_16SC1)) || (map2.type() == CV_16SC2 && (map1.type() == CV_16UC1 || map1.type() == CV_16SC1)) ) { if( map1.type() != CV_16SC2 ) std::swap(m1, m2); if( ifunc ) { ifunc( src, dst, *m1, *m2, ctab, borderType, borderValue ); return; } } else { CV_Assert( (map1.type() == CV_32FC2 && !map2.data) || (map1.type() == CV_32FC1 && map2.type() == CV_32FC1) ); planar_input = map1.channels() == 1; } 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); #if CV_SSE2 bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); #endif Mat _bufxy(brows0, bcols0, CV_16SC2), _bufa; if( !nnfunc ) _bufa.create(brows0, bcols0, CV_16UC1); for( y = 0; y < dst.rows; y += brows0 ) { for( x = 0; x < dst.cols; x += bcols0 ) { int brows = std::min(brows0, dst.rows - y); int bcols = std::min(bcols0, dst.cols - x); Mat dpart(dst, Rect(x, y, bcols, brows)); Mat bufxy(_bufxy, Rect(0, 0, bcols, brows)); if( nnfunc ) { if( map_depth != CV_32F ) { for( y1 = 0; y1 < brows; y1++ ) { short* XY = (short*)(bufxy.data + bufxy.step*y1); const short* sXY = (const short*)(m1->data + m1->step*(y+y1)) + x*2; const ushort* sA = (const ushort*)(m2->data + m2->step*(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 if( !planar_input ) map1(Rect(0,0,bcols,brows)).convertTo(bufxy, bufxy.depth()); else { for( y1 = 0; y1 < brows; y1++ ) { short* XY = (short*)(bufxy.data + bufxy.step*y1); const float* sX = (const float*)(map1.data + map1.step*(y+y1)) + x; const float* sY = (const float*)(map2.data + map2.step*(y+y1)) + x; x1 = 0; #if CV_SSE2 if( useSIMD ) { for( ; x1 <= bcols - 8; x1 += 8 ) { __m128 fx0 = _mm_loadu_ps(sX + x1); __m128 fx1 = _mm_loadu_ps(sX + x1 + 4); __m128 fy0 = _mm_loadu_ps(sY + x1); __m128 fy1 = _mm_loadu_ps(sY + x1 + 4); __m128i ix0 = _mm_cvtps_epi32(fx0); __m128i ix1 = _mm_cvtps_epi32(fx1); __m128i iy0 = _mm_cvtps_epi32(fy0); __m128i iy1 = _mm_cvtps_epi32(fy1); ix0 = _mm_packs_epi32(ix0, ix1); iy0 = _mm_packs_epi32(iy0, iy1); ix1 = _mm_unpacklo_epi16(ix0, iy0); iy1 = _mm_unpackhi_epi16(ix0, iy0); _mm_storeu_si128((__m128i*)(XY + x1*2), ix1); _mm_storeu_si128((__m128i*)(XY + x1*2 + 8), iy1); } } #endif for( ; x1 < bcols; x1++ ) { XY[x1*2] = saturate_cast(sX[x1]); XY[x1*2+1] = saturate_cast(sY[x1]); } } } nnfunc( src, dpart, bufxy, borderType, borderValue ); continue; } Mat bufa(_bufa, Rect(0,0,bcols, brows)); for( y1 = 0; y1 < brows; y1++ ) { short* XY = (short*)(bufxy.data + bufxy.step*y1); ushort* A = (ushort*)(bufa.data + bufa.step*y1); if( planar_input ) { const float* sX = (const float*)(map1.data + map1.step*(y+y1)) + x; const float* sY = (const float*)(map2.data + map2.step*(y+y1)) + x; x1 = 0; #if CV_SSE2 if( useSIMD ) { __m128 scale = _mm_set1_ps((float)INTER_TAB_SIZE); __m128i mask = _mm_set1_epi32(INTER_TAB_SIZE-1); for( ; x1 <= bcols - 8; x1 += 8 ) { __m128 fx0 = _mm_loadu_ps(sX + x1); __m128 fx1 = _mm_loadu_ps(sX + x1 + 4); __m128 fy0 = _mm_loadu_ps(sY + x1); __m128 fy1 = _mm_loadu_ps(sY + x1 + 4); __m128i ix0 = _mm_cvtps_epi32(_mm_mul_ps(fx0, scale)); __m128i ix1 = _mm_cvtps_epi32(_mm_mul_ps(fx1, scale)); __m128i iy0 = _mm_cvtps_epi32(_mm_mul_ps(fy0, scale)); __m128i iy1 = _mm_cvtps_epi32(_mm_mul_ps(fy1, scale)); __m128i mx0 = _mm_and_si128(ix0, mask); __m128i mx1 = _mm_and_si128(ix1, mask); __m128i my0 = _mm_and_si128(iy0, mask); __m128i my1 = _mm_and_si128(iy1, mask); mx0 = _mm_packs_epi32(mx0, mx1); my0 = _mm_packs_epi32(my0, my1); my0 = _mm_slli_epi16(my0, INTER_BITS); mx0 = _mm_or_si128(mx0, my0); _mm_storeu_si128((__m128i*)(A + x1), mx0); ix0 = _mm_srai_epi32(ix0, INTER_BITS); ix1 = _mm_srai_epi32(ix1, INTER_BITS); iy0 = _mm_srai_epi32(iy0, INTER_BITS); iy1 = _mm_srai_epi32(iy1, INTER_BITS); ix0 = _mm_packs_epi32(ix0, ix1); iy0 = _mm_packs_epi32(iy0, iy1); ix1 = _mm_unpacklo_epi16(ix0, iy0); iy1 = _mm_unpackhi_epi16(ix0, iy0); _mm_storeu_si128((__m128i*)(XY + x1*2), ix1); _mm_storeu_si128((__m128i*)(XY + x1*2 + 8), iy1); } } #endif for( ; x1 < bcols; x1++ ) { int sx = cvRound(sX[x1]*INTER_TAB_SIZE); int sy = cvRound(sY[x1]*INTER_TAB_SIZE); int v = (sy & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE-1)); XY[x1*2] = (short)(sx >> INTER_BITS); XY[x1*2+1] = (short)(sy >> INTER_BITS); A[x1] = (ushort)v; } } else { const float* sXY = (const float*)(map1.data + map1.step*(y+y1)) + x*2; for( x1 = 0; x1 < bcols; x1++ ) { int sx = cvRound(sXY[x1*2]*INTER_TAB_SIZE); int sy = cvRound(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] = (short)(sx >> INTER_BITS); XY[x1*2+1] = (short)(sy >> INTER_BITS); A[x1] = (ushort)v; } } } ifunc(src, dpart, bufxy, bufa, ctab, borderType, borderValue); } } } // out of the various interpolating functions, used by the above function for "ifunc", here's the bicubic variant as an example: template static void remapBicubic( const Mat& _src, Mat& _dst, const Mat& _xy, const Mat& _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.data; size_t sstep = _src.step/sizeof(S0[0]); Scalar_ cval(saturate_cast(_borderValue[0]), saturate_cast(_borderValue[1]), saturate_cast(_borderValue[2]), saturate_cast(_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); if( _dst.isContinuous() && _xy.isContinuous() && _fxy.isContinuous() ) { dsize.width *= dsize.height; dsize.height = 1; } for( dy = 0; dy < dsize.height; dy++ ) { T* D = (T*)(_dst.data + _dst.step*dy); const short* XY = (const short*)(_xy.data + _xy.step*dy); const ushort* FXY = (const ushort*)(_fxy.data + _fxy.step*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; } } } }