mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2191 lines
96 KiB
2191 lines
96 KiB
/* |
|
* By downloading, copying, installing or using the software you agree to this license. |
|
* If you do not agree to this license, do not download, install, |
|
* copy or use the software. |
|
* |
|
* |
|
* License Agreement |
|
* For Open Source Computer Vision Library |
|
* (3-clause BSD License) |
|
* |
|
* Copyright (C) 2015, NVIDIA Corporation, all rights reserved. |
|
* Third party copyrights are property of their respective owners. |
|
* |
|
* Redistribution and use in source and binary forms, with or without modification, |
|
* are permitted provided that the following conditions are met: |
|
* |
|
* * Redistributions of source code must retain the above copyright notice, |
|
* this list of conditions and the following disclaimer. |
|
* |
|
* * Redistributions in binary form must reproduce the above copyright notice, |
|
* this list of conditions and the following disclaimer in the documentation |
|
* and/or other materials provided with the distribution. |
|
* |
|
* * Neither the names of the copyright holders nor the names of the contributors |
|
* may be used to endorse or promote products derived from this software |
|
* without specific prior written permission. |
|
* |
|
* This software is provided by the copyright holders and contributors "as is" and |
|
* any express or implied warranties, including, but not limited to, the implied |
|
* warranties of merchantability and fitness for a particular purpose are disclaimed. |
|
* In no event shall copyright holders or contributors be liable for any direct, |
|
* indirect, incidental, special, exemplary, or consequential damages |
|
* (including, but not limited to, procurement of substitute goods or services; |
|
* loss of use, data, or profits; or business interruption) however caused |
|
* and on any theory of liability, whether in contract, strict liability, |
|
* or tort (including negligence or otherwise) arising in any way out of |
|
* the use of this software, even if advised of the possibility of such damage. |
|
*/ |
|
|
|
#include "common.hpp" |
|
#include "vtransform.hpp" |
|
|
|
#include <cmath> |
|
#include <vector> |
|
#include <algorithm> |
|
|
|
namespace CAROTENE_NS { |
|
|
|
bool isResizeNearestNeighborSupported(const Size2D &ssize, u32 elemSize) |
|
{ |
|
#if SIZE_MAX <= UINT32_MAX |
|
(void)ssize; |
|
#endif |
|
bool supportedElemSize = (elemSize == 1) || (elemSize == 3) || (elemSize == 4); |
|
return isSupportedConfiguration() |
|
#if SIZE_MAX > UINT32_MAX |
|
&& !(ssize.width > 0xffffFFFF || ssize.height > 0xffffFFFF)// Restrict image size since internally used resizeGeneric performs |
|
// index evaluation with u32 |
|
#endif |
|
&& supportedElemSize; |
|
} |
|
|
|
bool isResizeAreaSupported(f32 wr, f32 hr, u32 channels) |
|
{ |
|
bool supportedRatio = false; |
|
|
|
if (channels == 1) |
|
supportedRatio = (hr == wr) && ((wr == 2.0f) || (wr == 4.0f) || (wr == 0.5)); |
|
else if (channels == 3) |
|
supportedRatio = (hr == wr) && ((wr == 2.0f) || (wr == 4.0f) || (wr == 0.5f)); |
|
else if (channels == 4) |
|
supportedRatio = (hr == wr) && ((wr == 2.0f) || (wr == 4.0f) || (wr == 0.5f)); |
|
|
|
return isSupportedConfiguration() && supportedRatio; |
|
} |
|
|
|
bool isResizeLinearSupported(const Size2D &ssize, const Size2D &dsize, |
|
f32 wr, f32 hr, u32 channels) |
|
{ |
|
if ((wr <= 2.0f) && (hr <= 2.0f)) |
|
{ |
|
bool channelsSupport = (channels == 1) || (channels == 3) || (channels == 4); |
|
return (ssize.width >= 16) && (dsize.height >= 8) && |
|
(dsize.width >= 8) && channelsSupport; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool isResizeLinearOpenCVSupported(const Size2D &ssize, const Size2D &dsize, u32 channels) |
|
{ |
|
switch(channels) |
|
{ |
|
case 1: |
|
if (ssize.width >= 8 |
|
#if SIZE_MAX > UINT32_MAX |
|
&& !(ssize.width > 0xffffFFFF || ssize.height > 0xffffFFFF)// Restrict image size since internal index evaluation |
|
// is performed with u32 |
|
#endif |
|
&& dsize.width >= 8 && dsize.height >= 8) |
|
return isSupportedConfiguration(); |
|
return false; |
|
case 4: |
|
if (ssize.width >= 2 |
|
#if SIZE_MAX > UINT32_MAX |
|
&& !(ssize.width > 0xffffFFFF || ssize.height > 0xffffFFFF)// Restrict image size since internal index evaluation |
|
// is performed with u32 |
|
#endif |
|
&& dsize.width >= 2 && dsize.height >= 8) |
|
return isSupportedConfiguration(); |
|
default: |
|
return false; |
|
}; |
|
} |
|
|
|
#ifdef CAROTENE_NEON |
|
|
|
namespace { |
|
|
|
u32 * calcLUT(size_t size, f32 ratio, |
|
std::vector<u32> & _ofs) |
|
{ |
|
_ofs.resize(size); |
|
u32 * ofs = &_ofs[0]; |
|
|
|
size_t roiw8 = size >= 7 ? size - 7 : 0; |
|
size_t roiw4 = size >= 3 ? size - 3 : 0; |
|
size_t x = 0; |
|
|
|
f32 indeces[4] = { 0, 1, 2, 3 }; |
|
float32x4_t v_index = vld1q_f32(indeces), v_inc = vdupq_n_f32(4); |
|
float32x4_t v_05 = vdupq_n_f32(0.5f), v_ratio = vdupq_n_f32(ratio); |
|
|
|
for ( ; x < roiw8; x += 8) |
|
{ |
|
float32x4_t v_dstf = vmulq_f32(vaddq_f32(v_index, v_05), v_ratio); |
|
vst1q_u32(ofs + x, vcvtq_u32_f32(v_dstf)); |
|
v_index = vaddq_f32(v_index, v_inc); |
|
|
|
v_dstf = vmulq_f32(vaddq_f32(v_index, v_05), v_ratio); |
|
vst1q_u32(ofs + x + 4, vcvtq_u32_f32(v_dstf)); |
|
v_index = vaddq_f32(v_index, v_inc); |
|
} |
|
|
|
for ( ; x < roiw4; x += 4) |
|
{ |
|
float32x4_t v_dstf = vmulq_f32(vaddq_f32(v_index, v_05), v_ratio); |
|
vst1q_u32(ofs + x, vcvtq_u32_f32(v_dstf)); |
|
v_index = vaddq_f32(v_index, v_inc); |
|
} |
|
|
|
for ( ; x < size; ++x) |
|
{ |
|
ofs[x] = static_cast<u32>(floorf((x + 0.5f) * ratio)); |
|
} |
|
|
|
return ofs; |
|
} |
|
|
|
template <typename T> |
|
void resizeGeneric(const Size2D &dsize, |
|
const void * srcBase, ptrdiff_t srcStride, |
|
void * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr) |
|
{ |
|
std::vector<u32> _x_ofs; |
|
u32 * x_ofs = calcLUT(dsize.width, wr, _x_ofs);//32bit LUT is used so we could get issues on src image dimensions greater than (2^32-1) |
|
|
|
for (size_t dst_y = 0; dst_y < dsize.height; ++dst_y) |
|
{ |
|
size_t src_y = static_cast<size_t>(floorf((dst_y + 0.5f) * hr)); |
|
const T * src = internal::getRowPtr(static_cast<const T *>(srcBase), srcStride, src_y); |
|
T * dst = internal::getRowPtr(static_cast<T *>(dstBase), dstStride, dst_y); |
|
|
|
for (size_t dst_x = 0; dst_x < dsize.width; ++dst_x) |
|
{ |
|
internal::prefetch(src + dst_x); |
|
dst[dst_x] = src[x_ofs[dst_x]]; |
|
} |
|
} |
|
} |
|
|
|
typedef struct _24bit_ |
|
{ |
|
u8 a[3]; |
|
} _24bit; |
|
|
|
} // namespace |
|
|
|
|
|
#endif |
|
|
|
void resizeNearestNeighbor(const Size2D &ssize, const Size2D &dsize, |
|
const void * srcBase, ptrdiff_t srcStride, |
|
void * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr, u32 elemSize) |
|
{ |
|
internal::assertSupportedConfiguration(wr > 0 && hr > 0 && |
|
(dsize.width - 0.5) * wr < ssize.width && |
|
(dsize.height - 0.5) * hr < ssize.height && // Ensure we have enough source data |
|
(dsize.width + 0.5) * wr >= ssize.width && |
|
(dsize.height + 0.5) * hr >= ssize.height && // Ensure source isn't too big |
|
isResizeNearestNeighborSupported(ssize, elemSize)); |
|
#ifdef CAROTENE_NEON |
|
|
|
if (elemSize == 1) |
|
{ |
|
resizeGeneric<u8>(dsize, |
|
srcBase, srcStride, |
|
dstBase, dstStride, |
|
wr, hr); |
|
} |
|
else if (elemSize == 3) |
|
{ |
|
resizeGeneric<_24bit>(dsize, |
|
srcBase, srcStride, |
|
dstBase, dstStride, |
|
wr, hr); |
|
} |
|
else if (elemSize == 4) |
|
{ |
|
resizeGeneric<u32>(dsize, |
|
srcBase, srcStride, |
|
dstBase, dstStride, |
|
wr, hr); |
|
} |
|
|
|
#else |
|
(void)dsize; |
|
(void)srcBase; |
|
(void)srcStride; |
|
(void)dstBase; |
|
(void)dstStride; |
|
(void)wr; |
|
(void)hr; |
|
#endif |
|
} |
|
|
|
#ifdef CAROTENE_NEON |
|
template <bool opencv_like, int shiftsize> |
|
inline uint8x8_t areaDownsamplingDivision(uint16x8_t data) |
|
{ |
|
return vshrn_n_u16(data, shiftsize); |
|
} |
|
template <> |
|
inline uint8x8_t areaDownsamplingDivision<true,2>(uint16x8_t data) |
|
{ |
|
// rounding |
|
return vrshrn_n_u16(data,2); |
|
} |
|
template <> |
|
inline uint8x8_t areaDownsamplingDivision<true,4>(uint16x8_t data) |
|
{ |
|
// bankers rounding |
|
return vrshrn_n_u16(vqsubq_u16(data, vshrq_n_u16(vbicq_u16(vdupq_n_u16(1<<4), data), 4)),4); |
|
} |
|
|
|
template <bool opencv_like, int shiftsize> |
|
inline u8 areaDownsamplingDivision(u16 data) |
|
{ |
|
return data >> shiftsize; |
|
} |
|
template <> |
|
inline u8 areaDownsamplingDivision<true,2>(u16 data) |
|
{ |
|
// rounding |
|
return (data + 2) >> 2; |
|
} |
|
template <> |
|
inline u8 areaDownsamplingDivision<true,4>(u16 data) |
|
{ |
|
// bankers rounding |
|
return (data - (((1<<4) & ~data) >> 4) + 8) >> 4; |
|
} |
|
#endif |
|
|
|
template <bool opencv_like> |
|
inline void resizeAreaRounding(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr, u32 channels) |
|
{ |
|
internal::assertSupportedConfiguration(isResizeAreaSupported(wr, hr, channels) && |
|
std::abs(dsize.width * wr - ssize.width) < 0.1 && |
|
std::abs(dsize.height * hr - ssize.height) < 0.1); |
|
#ifdef CAROTENE_NEON |
|
if (channels == 1) |
|
{ |
|
if ((wr == 2.0f) && (hr == 2.0f)) |
|
{ |
|
size_t roiw8 = dsize.width >= 7 ? dsize.width - 7 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; ++i) |
|
{ |
|
const u8 * src0_row = internal::getRowPtr(srcBase, srcStride, i << 1); |
|
const u8 * src1_row = internal::getRowPtr(srcBase, srcStride, (i << 1) + 1); |
|
u8 * dst_row = internal::getRowPtr(dstBase, dstStride, i); |
|
size_t sj = 0, dj = 0; |
|
|
|
for ( ; dj < roiw8; dj += 8, sj += 16) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
|
|
uint16x8_t vSum1 = vpaddlq_u8(vld1q_u8(src0_row + sj)); |
|
uint16x8_t vSum2 = vpaddlq_u8(vld1q_u8(src1_row + sj)); |
|
uint8x8_t vRes1 = areaDownsamplingDivision<opencv_like,2>(vaddq_u16(vSum1, vSum2)); |
|
|
|
vst1_u8(dst_row + dj, vRes1); |
|
} |
|
|
|
for ( ; dj < dsize.width; ++dj, sj += 2) |
|
{ |
|
dst_row[dj] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj] + src0_row[sj + 1] + |
|
src1_row[sj] + src1_row[sj + 1]); |
|
} |
|
} |
|
} |
|
else if ((wr == 0.5f) && (hr == 0.5f)) |
|
{ |
|
size_t roiw32 = dsize.width >= 31 ? dsize.width - 31 : 0; |
|
size_t roiw16 = dsize.width >= 15 ? dsize.width - 15 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; i += 2) |
|
{ |
|
const u8 * src_row = internal::getRowPtr(srcBase, srcStride, i >> 1); |
|
u8 * dst0_row = internal::getRowPtr(dstBase, dstStride, i); |
|
u8 * dst1_row = internal::getRowPtr(dstBase, dstStride, std::min(i + 1, dsize.height - 1)); |
|
size_t sj = 0, dj = 0; |
|
|
|
for ( ; dj < roiw32; dj += 32, sj += 16) |
|
{ |
|
internal::prefetch(src_row + sj); |
|
|
|
uint8x16x2_t v_dst; |
|
v_dst.val[0] = v_dst.val[1] = vld1q_u8(src_row + sj); |
|
|
|
vst2q_u8(dst0_row + dj, v_dst); |
|
vst2q_u8(dst1_row + dj, v_dst); |
|
} |
|
|
|
for ( ; dj < roiw16; dj += 16, sj += 8) |
|
{ |
|
uint8x8x2_t v_dst; |
|
v_dst.val[0] = v_dst.val[1] = vld1_u8(src_row + sj); |
|
|
|
vst2_u8(dst0_row + dj, v_dst); |
|
vst2_u8(dst1_row + dj, v_dst); |
|
} |
|
|
|
for ( ; dj < dsize.width; dj += 2, ++sj) |
|
{ |
|
u8 src_val = src_row[sj]; |
|
dst0_row[dj] = dst0_row[dj + 1] = src_val; |
|
dst1_row[dj] = dst1_row[dj + 1] = src_val; |
|
} |
|
} |
|
} |
|
else //if ((wr == 4.0f) && (hr == 4.0f)) //the only scale that lasts after isSupported check |
|
{ |
|
#ifndef ANDROID |
|
size_t roiw16 = dsize.width >= 15 ? dsize.width - 15 : 0; |
|
#endif |
|
size_t roiw8 = dsize.width >= 7 ? dsize.width - 7 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; ++i) |
|
{ |
|
const u8 * src0_row = internal::getRowPtr(srcBase, srcStride, i << 2); |
|
const u8 * src1_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 1); |
|
const u8 * src2_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 2); |
|
const u8 * src3_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 3); |
|
u8 * dst_row = internal::getRowPtr(dstBase, dstStride, i); |
|
size_t sj = 0, dj = 0; |
|
|
|
#ifndef ANDROID |
|
for ( ; dj < roiw16; dj += 16, sj += 64) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
internal::prefetch(src2_row + sj); |
|
internal::prefetch(src3_row + sj); |
|
|
|
uint8x16x4_t vLane1 = vld4q_u8(src0_row + sj); |
|
uint8x16x4_t vLane2 = vld4q_u8(src1_row + sj); |
|
uint8x16x4_t vLane3 = vld4q_u8(src2_row + sj); |
|
uint8x16x4_t vLane4 = vld4q_u8(src3_row + sj); |
|
|
|
uint16x8_t vSum_0 = vaddl_u8(vget_low_u8(vLane1.val[0]), vget_low_u8(vLane1.val[1])); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane1.val[2]), vget_low_u8(vLane1.val[3]))); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane2.val[0]), vget_low_u8(vLane2.val[1]))); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane2.val[2]), vget_low_u8(vLane2.val[3]))); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane3.val[0]), vget_low_u8(vLane3.val[1]))); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane3.val[2]), vget_low_u8(vLane3.val[3]))); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane4.val[0]), vget_low_u8(vLane4.val[1]))); |
|
vSum_0 = vaddq_u16(vSum_0, vaddl_u8(vget_low_u8(vLane4.val[2]), vget_low_u8(vLane4.val[3]))); |
|
|
|
uint16x8_t vSum_1 = vaddl_u8(vget_high_u8(vLane1.val[0]), vget_high_u8(vLane1.val[1])); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane1.val[2]), vget_high_u8(vLane1.val[3]))); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane2.val[0]), vget_high_u8(vLane2.val[1]))); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane2.val[2]), vget_high_u8(vLane2.val[3]))); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane3.val[0]), vget_high_u8(vLane3.val[1]))); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane3.val[2]), vget_high_u8(vLane3.val[3]))); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane4.val[0]), vget_high_u8(vLane4.val[1]))); |
|
vSum_1 = vaddq_u16(vSum_1, vaddl_u8(vget_high_u8(vLane4.val[2]), vget_high_u8(vLane4.val[3]))); |
|
|
|
uint8x8_t vRes_0 = areaDownsamplingDivision<opencv_like,4>(vSum_0); |
|
uint8x8_t vRes_1 = areaDownsamplingDivision<opencv_like,4>(vSum_1); |
|
|
|
vst1q_u8(dst_row + dj, vcombine_u8(vRes_0, vRes_1)); |
|
} |
|
#endif |
|
|
|
for ( ; dj < roiw8; dj += 8, sj += 32) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
internal::prefetch(src2_row + sj); |
|
internal::prefetch(src3_row + sj); |
|
|
|
uint8x8x4_t vLane1 = vld4_u8(src0_row + sj); |
|
uint8x8x4_t vLane2 = vld4_u8(src1_row + sj); |
|
uint8x8x4_t vLane3 = vld4_u8(src2_row + sj); |
|
uint8x8x4_t vLane4 = vld4_u8(src3_row + sj); |
|
|
|
uint16x8_t vSum = vaddl_u8(vLane1.val[0], vLane1.val[1]); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane1.val[2], vLane1.val[3])); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane2.val[0], vLane2.val[1])); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane2.val[2], vLane2.val[3])); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane3.val[0], vLane3.val[1])); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane3.val[2], vLane3.val[3])); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane4.val[0], vLane4.val[1])); |
|
vSum = vaddq_u16(vSum, vaddl_u8(vLane4.val[2], vLane4.val[3])); |
|
|
|
vst1_u8(dst_row + dj, (areaDownsamplingDivision<opencv_like,4>(vSum))); |
|
} |
|
|
|
for ( ; dj < dsize.width; ++dj, sj += 4) |
|
{ |
|
dst_row[dj] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj] + src0_row[sj + 1] + src0_row[sj + 2] + src0_row[sj + 3] + |
|
src1_row[sj] + src1_row[sj + 1] + src1_row[sj + 2] + src1_row[sj + 3] + |
|
src2_row[sj] + src2_row[sj + 1] + src2_row[sj + 2] + src2_row[sj + 3] + |
|
src3_row[sj] + src3_row[sj + 1] + src3_row[sj + 2] + src3_row[sj + 3]); |
|
} |
|
} |
|
} |
|
} |
|
else if (channels == 4) |
|
{ |
|
if ((wr == 2.0f) && (hr == 2.0f)) |
|
{ |
|
#ifndef ANDROID |
|
size_t roiw4 = dsize.width >= 3 ? (dsize.width - 3) << 2 : 0; |
|
#endif |
|
size_t roiw2 = dsize.width >= 1 ? (dsize.width - 1) << 2 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; ++i) |
|
{ |
|
const u8 * src0_row = internal::getRowPtr(srcBase, srcStride, i << 1); |
|
const u8 * src1_row = internal::getRowPtr(srcBase, srcStride, (i << 1) + 1); |
|
u8 * dst_row = internal::getRowPtr(dstBase, dstStride, i); |
|
size_t sj = 0, dj = 0; |
|
|
|
#ifndef ANDROID |
|
for ( ; dj < roiw4; dj += 16, sj += 32) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
|
|
uint8x8_t vRes_0, vRes_1; |
|
|
|
{ |
|
uint8x16_t vLane1 = vld1q_u8(src0_row + sj); |
|
uint8x16_t vLane2 = vld1q_u8(src1_row + sj); |
|
|
|
uint16x8_t vLane_l = vaddl_u8(vget_low_u8(vLane1), vget_low_u8(vLane2)); |
|
uint16x8_t vLane_h = vaddl_u8(vget_high_u8(vLane1), vget_high_u8(vLane2)); |
|
|
|
uint16x4_t vSum_l = vadd_u16(vget_low_u16(vLane_l), vget_high_u16(vLane_l)); |
|
uint16x4_t vSum_h = vadd_u16(vget_low_u16(vLane_h), vget_high_u16(vLane_h)); |
|
|
|
vRes_0 = areaDownsamplingDivision<opencv_like,2>(vcombine_u16(vSum_l, vSum_h)); |
|
} |
|
|
|
{ |
|
uint8x16_t vLane1 = vld1q_u8(src0_row + sj + 16); |
|
uint8x16_t vLane2 = vld1q_u8(src1_row + sj + 16); |
|
|
|
uint16x8_t vLane_l = vaddl_u8(vget_low_u8(vLane1), vget_low_u8(vLane2)); |
|
uint16x8_t vLane_h = vaddl_u8(vget_high_u8(vLane1), vget_high_u8(vLane2)); |
|
|
|
uint16x4_t vSum_l = vadd_u16(vget_low_u16(vLane_l), vget_high_u16(vLane_l)); |
|
uint16x4_t vSum_h = vadd_u16(vget_low_u16(vLane_h), vget_high_u16(vLane_h)); |
|
|
|
vRes_1 = areaDownsamplingDivision<opencv_like,2>(vcombine_u16(vSum_l, vSum_h)); |
|
} |
|
|
|
vst1q_u8(dst_row + dj, vcombine_u8(vRes_0, vRes_1)); |
|
} |
|
#endif |
|
|
|
for ( ; dj < roiw2; dj += 8, sj += 16) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
|
|
uint8x16_t vLane1 = vld1q_u8(src0_row + sj); |
|
uint8x16_t vLane2 = vld1q_u8(src1_row + sj); |
|
|
|
uint16x8_t vLane_l = vaddl_u8(vget_low_u8(vLane1), vget_low_u8(vLane2)); |
|
uint16x8_t vLane_h = vaddl_u8(vget_high_u8(vLane1), vget_high_u8(vLane2)); |
|
|
|
uint16x4_t vSum_l = vadd_u16(vget_low_u16(vLane_l), vget_high_u16(vLane_l)); |
|
uint16x4_t vSum_h = vadd_u16(vget_low_u16(vLane_h), vget_high_u16(vLane_h)); |
|
|
|
uint8x8_t vRes = areaDownsamplingDivision<opencv_like,2>(vcombine_u16(vSum_l, vSum_h)); |
|
vst1_u8(dst_row + dj, vRes); |
|
} |
|
|
|
for (size_t dwidth = dsize.width << 2; dj < dwidth; dj += 4, sj += 8) |
|
{ |
|
dst_row[dj ] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj ] + src0_row[sj + 4] + |
|
src1_row[sj ] + src1_row[sj + 4]); |
|
dst_row[dj + 1] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj + 1] + src0_row[sj + 5] + |
|
src1_row[sj + 1] + src1_row[sj + 5]); |
|
dst_row[dj + 2] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj + 2] + src0_row[sj + 6] + |
|
src1_row[sj + 2] + src1_row[sj + 6]); |
|
dst_row[dj + 3] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj + 3] + src0_row[sj + 7] + |
|
src1_row[sj + 3] + src1_row[sj + 7]); |
|
} |
|
} |
|
} |
|
else if ((wr == 0.5f) && (hr == 0.5f)) |
|
{ |
|
#ifndef ANDROID |
|
size_t roiw32 = dsize.width >= 31 ? (dsize.width - 31) << 2 : 0; |
|
#endif |
|
size_t roiw16 = dsize.width >= 15 ? (dsize.width - 15) << 2 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; i += 2) |
|
{ |
|
const u8 * src_row = internal::getRowPtr(srcBase, srcStride, i >> 1); |
|
u8 * dst0_row = internal::getRowPtr(dstBase, dstStride, i); |
|
u8 * dst1_row = internal::getRowPtr(dstBase, dstStride, std::min(i + 1, dsize.height - 1)); |
|
size_t sj = 0, dj = 0; |
|
|
|
#ifndef ANDROID |
|
for ( ; dj < roiw32; dj += 128, sj += 64) |
|
{ |
|
internal::prefetch(src_row + sj); |
|
|
|
uint8x16x4_t v_src = vld4q_u8(src_row + sj); |
|
uint8x16x2_t v_c0 = vzipq_u8(v_src.val[0], v_src.val[0]); |
|
uint8x16x2_t v_c1 = vzipq_u8(v_src.val[1], v_src.val[1]); |
|
uint8x16x2_t v_c2 = vzipq_u8(v_src.val[2], v_src.val[2]); |
|
uint8x16x2_t v_c3 = vzipq_u8(v_src.val[3], v_src.val[3]); |
|
|
|
uint8x16x4_t v_dst; |
|
v_dst.val[0] = v_c0.val[0]; |
|
v_dst.val[1] = v_c1.val[0]; |
|
v_dst.val[2] = v_c2.val[0]; |
|
v_dst.val[3] = v_c3.val[0]; |
|
vst4q_u8(dst0_row + dj, v_dst); |
|
vst4q_u8(dst1_row + dj, v_dst); |
|
|
|
v_dst.val[0] = v_c0.val[1]; |
|
v_dst.val[1] = v_c1.val[1]; |
|
v_dst.val[2] = v_c2.val[1]; |
|
v_dst.val[3] = v_c3.val[1]; |
|
vst4q_u8(dst0_row + dj + 64, v_dst); |
|
vst4q_u8(dst1_row + dj + 64, v_dst); |
|
} |
|
#endif |
|
|
|
for ( ; dj < roiw16; dj += 64, sj += 32) |
|
{ |
|
internal::prefetch(src_row + sj); |
|
|
|
uint8x8x4_t v_src = vld4_u8(src_row + sj); |
|
uint8x8x2_t v_c0 = vzip_u8(v_src.val[0], v_src.val[0]); |
|
uint8x8x2_t v_c1 = vzip_u8(v_src.val[1], v_src.val[1]); |
|
uint8x8x2_t v_c2 = vzip_u8(v_src.val[2], v_src.val[2]); |
|
uint8x8x2_t v_c3 = vzip_u8(v_src.val[3], v_src.val[3]); |
|
|
|
uint8x16x4_t v_dst; |
|
v_dst.val[0] = vcombine_u8(v_c0.val[0], v_c0.val[1]); |
|
v_dst.val[1] = vcombine_u8(v_c1.val[0], v_c1.val[1]); |
|
v_dst.val[2] = vcombine_u8(v_c2.val[0], v_c2.val[1]); |
|
v_dst.val[3] = vcombine_u8(v_c3.val[0], v_c3.val[1]); |
|
vst4q_u8(dst0_row + dj, v_dst); |
|
vst4q_u8(dst1_row + dj, v_dst); |
|
} |
|
|
|
for (size_t dwidth = dsize.width << 2; dj < dwidth; dj += 8, sj += 4) |
|
{ |
|
u8 src_val = src_row[sj]; |
|
dst0_row[dj] = dst0_row[dj + 4] = src_val; |
|
dst1_row[dj] = dst1_row[dj + 4] = src_val; |
|
|
|
src_val = src_row[sj + 1]; |
|
dst0_row[dj + 1] = dst0_row[dj + 5] = src_val; |
|
dst1_row[dj + 1] = dst1_row[dj + 5] = src_val; |
|
|
|
src_val = src_row[sj + 2]; |
|
dst0_row[dj + 2] = dst0_row[dj + 6] = src_val; |
|
dst1_row[dj + 2] = dst1_row[dj + 6] = src_val; |
|
|
|
src_val = src_row[sj + 3]; |
|
dst0_row[dj + 3] = dst0_row[dj + 7] = src_val; |
|
dst1_row[dj + 3] = dst1_row[dj + 7] = src_val; |
|
} |
|
} |
|
} |
|
else //if ((hr == 4.0f) && (wr == 4.0f)) //the only scale that lasts after isSupported check |
|
{ |
|
size_t roiw4 = dsize.width >= 3 ? (dsize.width - 3) << 2 : 0; |
|
size_t roiw2 = dsize.width >= 1 ? (dsize.width - 1) << 2 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; ++i) |
|
{ |
|
const u8 * src0_row = internal::getRowPtr(srcBase, srcStride, i << 2); |
|
const u8 * src1_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 1); |
|
const u8 * src2_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 2); |
|
const u8 * src3_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 3); |
|
u8 * dst_row = internal::getRowPtr(dstBase, dstStride, i); |
|
size_t sj = 0, dj = 0; |
|
|
|
for ( ; dj < roiw4; dj += 16, sj += 64) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
internal::prefetch(src2_row + sj); |
|
internal::prefetch(src3_row + sj); |
|
|
|
uint8x16_t vLane10 = vld1q_u8(src0_row + sj), vLane11 = vld1q_u8(src0_row + sj + 16); |
|
uint8x16_t vLane20 = vld1q_u8(src1_row + sj), vLane21 = vld1q_u8(src1_row + sj + 16); |
|
uint8x16_t vLane30 = vld1q_u8(src2_row + sj), vLane31 = vld1q_u8(src2_row + sj + 16); |
|
uint8x16_t vLane40 = vld1q_u8(src3_row + sj), vLane41 = vld1q_u8(src3_row + sj + 16); |
|
|
|
uint16x8_t v_part_0, v_part_1; |
|
{ |
|
uint16x8_t v_sum0 = vaddl_u8(vget_low_u8(vLane10), vget_high_u8(vLane10)); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane20), vget_high_u8(vLane20))); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane30), vget_high_u8(vLane30))); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane40), vget_high_u8(vLane40))); |
|
|
|
uint16x8_t v_sum1 = vaddl_u8(vget_low_u8(vLane11), vget_high_u8(vLane11)); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane21), vget_high_u8(vLane21))); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane31), vget_high_u8(vLane31))); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane41), vget_high_u8(vLane41))); |
|
|
|
v_part_0 = vcombine_u16(vadd_u16(vget_low_u16(v_sum0), vget_high_u16(v_sum0)), |
|
vadd_u16(vget_low_u16(v_sum1), vget_high_u16(v_sum1))); |
|
} |
|
|
|
vLane10 = vld1q_u8(src0_row + sj + 32); |
|
vLane11 = vld1q_u8(src0_row + sj + 48); |
|
vLane20 = vld1q_u8(src1_row + sj + 32); |
|
vLane21 = vld1q_u8(src1_row + sj + 48); |
|
vLane30 = vld1q_u8(src2_row + sj + 32); |
|
vLane31 = vld1q_u8(src2_row + sj + 48); |
|
vLane40 = vld1q_u8(src3_row + sj + 32); |
|
vLane41 = vld1q_u8(src3_row + sj + 48); |
|
|
|
{ |
|
uint16x8_t v_sum0 = vaddl_u8(vget_low_u8(vLane10), vget_high_u8(vLane10)); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane20), vget_high_u8(vLane20))); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane30), vget_high_u8(vLane30))); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane40), vget_high_u8(vLane40))); |
|
|
|
uint16x8_t v_sum1 = vaddl_u8(vget_low_u8(vLane11), vget_high_u8(vLane11)); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane21), vget_high_u8(vLane21))); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane31), vget_high_u8(vLane31))); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane41), vget_high_u8(vLane41))); |
|
|
|
v_part_1 = vcombine_u16(vadd_u16(vget_low_u16(v_sum0), vget_high_u16(v_sum0)), |
|
vadd_u16(vget_low_u16(v_sum1), vget_high_u16(v_sum1))); |
|
} |
|
|
|
vst1q_u8(dst_row + dj, vcombine_u8(areaDownsamplingDivision<opencv_like,4>(v_part_0), |
|
areaDownsamplingDivision<opencv_like,4>(v_part_1))); |
|
} |
|
|
|
for ( ; dj < roiw2; dj += 8, sj += 32) |
|
{ |
|
uint8x16_t vLane10 = vld1q_u8(src0_row + sj), vLane11 = vld1q_u8(src0_row + sj + 16); |
|
uint8x16_t vLane20 = vld1q_u8(src1_row + sj), vLane21 = vld1q_u8(src1_row + sj + 16); |
|
uint8x16_t vLane30 = vld1q_u8(src2_row + sj), vLane31 = vld1q_u8(src2_row + sj + 16); |
|
uint8x16_t vLane40 = vld1q_u8(src3_row + sj), vLane41 = vld1q_u8(src3_row + sj + 16); |
|
|
|
uint16x8_t v_sum0 = vaddl_u8(vget_low_u8(vLane10), vget_high_u8(vLane10)); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane20), vget_high_u8(vLane20))); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane30), vget_high_u8(vLane30))); |
|
v_sum0 = vaddq_u16(v_sum0, vaddl_u8(vget_low_u8(vLane40), vget_high_u8(vLane40))); |
|
|
|
uint16x8_t v_sum1 = vaddl_u8(vget_low_u8(vLane11), vget_high_u8(vLane11)); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane21), vget_high_u8(vLane21))); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane31), vget_high_u8(vLane31))); |
|
v_sum1 = vaddq_u16(v_sum1, vaddl_u8(vget_low_u8(vLane41), vget_high_u8(vLane41))); |
|
|
|
uint16x8_t v_sum = vcombine_u16(vadd_u16(vget_low_u16(v_sum0), vget_high_u16(v_sum0)), |
|
vadd_u16(vget_low_u16(v_sum1), vget_high_u16(v_sum1))); |
|
|
|
vst1_u8(dst_row + dj, (areaDownsamplingDivision<opencv_like,4>(v_sum))); |
|
} |
|
|
|
for (size_t dwidth = dsize.width << 2; dj < dwidth; dj += 4, sj += 16) |
|
{ |
|
dst_row[dj ] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj ] + src0_row[sj + 4] + |
|
src0_row[sj + 8] + src0_row[sj + 12] + |
|
src1_row[sj ] + src1_row[sj + 4] + |
|
src1_row[sj + 8] + src1_row[sj + 12] + |
|
src2_row[sj ] + src2_row[sj + 4] + |
|
src2_row[sj + 8] + src2_row[sj + 12] + |
|
src3_row[sj ] + src3_row[sj + 4] + |
|
src3_row[sj + 8] + src3_row[sj + 12]); |
|
|
|
dst_row[dj + 1] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj + 1] + src0_row[sj + 5] + |
|
src0_row[sj + 9] + src0_row[sj + 13] + |
|
src1_row[sj + 1] + src1_row[sj + 5] + |
|
src1_row[sj + 9] + src1_row[sj + 13] + |
|
src2_row[sj + 1] + src2_row[sj + 5] + |
|
src2_row[sj + 9] + src2_row[sj + 13] + |
|
src3_row[sj + 1] + src3_row[sj + 5] + |
|
src3_row[sj + 9] + src3_row[sj + 13]); |
|
|
|
dst_row[dj + 2] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj + 2] + src0_row[sj + 6] + |
|
src0_row[sj + 10] + src0_row[sj + 14] + |
|
src1_row[sj + 2] + src1_row[sj + 6] + |
|
src1_row[sj + 10] + src1_row[sj + 14] + |
|
src2_row[sj + 2] + src2_row[sj + 6] + |
|
src2_row[sj + 10] + src2_row[sj + 14] + |
|
src3_row[sj + 2] + src3_row[sj + 6] + |
|
src3_row[sj + 10] + src3_row[sj + 14]); |
|
|
|
dst_row[dj + 3] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj + 3] + src0_row[sj + 7] + |
|
src0_row[sj + 11] + src0_row[sj + 15] + |
|
src1_row[sj + 3] + src1_row[sj + 7] + |
|
src1_row[sj + 11] + src1_row[sj + 15] + |
|
src2_row[sj + 3] + src2_row[sj + 7] + |
|
src2_row[sj + 11] + src2_row[sj + 15] + |
|
src3_row[sj + 3] + src3_row[sj + 7] + |
|
src3_row[sj + 11] + src3_row[sj + 15]); |
|
} |
|
} |
|
} |
|
} |
|
else if (channels == 3) |
|
{ |
|
if ((wr == 2.0f) && (wr == 2.0f)) |
|
{ |
|
#ifndef ANDROID |
|
size_t roiw16 = dsize.width >= 15 ? (dsize.width - 15) * 3 : 0; |
|
#endif |
|
size_t roiw8 = dsize.width >= 7 ? (dsize.width - 7) * 3 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; ++i) |
|
{ |
|
const u8 * src0_row = internal::getRowPtr(srcBase, srcStride, i << 1); |
|
const u8 * src1_row = internal::getRowPtr(srcBase, srcStride, (i << 1) + 1); |
|
u8 * dst_row = internal::getRowPtr(dstBase, dstStride, i); |
|
size_t sj = 0, dj = 0; |
|
|
|
#ifndef ANDROID |
|
for ( ; dj < roiw16; dj += 48, sj += 96) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
|
|
uint8x16x3_t vLane1 = vld3q_u8(src0_row + sj); |
|
uint8x16x3_t vLane2 = vld3q_u8(src1_row + sj); |
|
|
|
uint8x8x3_t v_dst0, v_dst1; |
|
{ |
|
uint16x8_t v_el0 = vpaddlq_u8(vLane1.val[0]); |
|
uint16x8_t v_el1 = vpaddlq_u8(vLane1.val[1]); |
|
uint16x8_t v_el2 = vpaddlq_u8(vLane1.val[2]); |
|
v_el0 = vpadalq_u8(v_el0, vLane2.val[0]); |
|
v_el1 = vpadalq_u8(v_el1, vLane2.val[1]); |
|
v_el2 = vpadalq_u8(v_el2, vLane2.val[2]); |
|
|
|
v_dst0.val[0] = areaDownsamplingDivision<opencv_like,2>(v_el0); |
|
v_dst0.val[1] = areaDownsamplingDivision<opencv_like,2>(v_el1); |
|
v_dst0.val[2] = areaDownsamplingDivision<opencv_like,2>(v_el2); |
|
} |
|
|
|
vLane1 = vld3q_u8(src0_row + sj + 48); |
|
vLane2 = vld3q_u8(src1_row + sj + 48); |
|
{ |
|
uint16x8_t v_el0 = vpaddlq_u8(vLane1.val[0]); |
|
uint16x8_t v_el1 = vpaddlq_u8(vLane1.val[1]); |
|
uint16x8_t v_el2 = vpaddlq_u8(vLane1.val[2]); |
|
v_el0 = vpadalq_u8(v_el0, vLane2.val[0]); |
|
v_el1 = vpadalq_u8(v_el1, vLane2.val[1]); |
|
v_el2 = vpadalq_u8(v_el2, vLane2.val[2]); |
|
|
|
v_dst1.val[0] = areaDownsamplingDivision<opencv_like,2>(v_el0); |
|
v_dst1.val[1] = areaDownsamplingDivision<opencv_like,2>(v_el1); |
|
v_dst1.val[2] = areaDownsamplingDivision<opencv_like,2>(v_el2); |
|
} |
|
|
|
uint8x16x3_t v_dst; |
|
v_dst.val[0] = vcombine_u8(v_dst0.val[0], v_dst1.val[0]); |
|
v_dst.val[1] = vcombine_u8(v_dst0.val[1], v_dst1.val[1]); |
|
v_dst.val[2] = vcombine_u8(v_dst0.val[2], v_dst1.val[2]); |
|
|
|
vst3q_u8(dst_row + dj, v_dst); |
|
} |
|
#endif |
|
|
|
for ( ; dj < roiw8; dj += 24, sj += 48) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
|
|
uint8x16x3_t vLane1 = vld3q_u8(src0_row + sj); |
|
uint8x16x3_t vLane2 = vld3q_u8(src1_row + sj); |
|
|
|
uint16x8_t v_el0 = vpaddlq_u8(vLane1.val[0]); |
|
uint16x8_t v_el1 = vpaddlq_u8(vLane1.val[1]); |
|
uint16x8_t v_el2 = vpaddlq_u8(vLane1.val[2]); |
|
v_el0 = vpadalq_u8(v_el0, vLane2.val[0]); |
|
v_el1 = vpadalq_u8(v_el1, vLane2.val[1]); |
|
v_el2 = vpadalq_u8(v_el2, vLane2.val[2]); |
|
|
|
uint8x8x3_t v_dst; |
|
v_dst.val[0] = areaDownsamplingDivision<opencv_like,2>(v_el0); |
|
v_dst.val[1] = areaDownsamplingDivision<opencv_like,2>(v_el1); |
|
v_dst.val[2] = areaDownsamplingDivision<opencv_like,2>(v_el2); |
|
|
|
vst3_u8(dst_row + dj, v_dst); |
|
} |
|
|
|
for (size_t dwidth = dsize.width * 3; dj < dwidth; dj += 3, sj += 6) |
|
{ |
|
dst_row[dj ] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj ] + src0_row[sj + 3] + |
|
src1_row[sj ] + src1_row[sj + 3]); |
|
dst_row[dj + 1] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj + 1] + src0_row[sj + 4] + |
|
src1_row[sj + 1] + src1_row[sj + 4]); |
|
dst_row[dj + 2] = areaDownsamplingDivision<opencv_like,2>( |
|
(u16)src0_row[sj + 2] + src0_row[sj + 5] + |
|
src1_row[sj + 2] + src1_row[sj + 5]); |
|
} |
|
} |
|
} |
|
else if ((wr == 0.5f) && (hr == 0.5f)) |
|
{ |
|
#ifndef ANDROID |
|
size_t roiw32 = dsize.width >= 31 ? (dsize.width - 31) * 3 : 0; |
|
#endif |
|
size_t roiw16 = dsize.width >= 15 ? (dsize.width - 15) * 3 : 0; |
|
|
|
for (size_t i = 0; i < dsize.height; i += 2) |
|
{ |
|
const u8 * src_row = internal::getRowPtr(srcBase, srcStride, i >> 1); |
|
u8 * dst0_row = internal::getRowPtr(dstBase, dstStride, i); |
|
u8 * dst1_row = internal::getRowPtr(dstBase, dstStride, std::min(i + 1, dsize.height - 1)); |
|
size_t sj = 0, dj = 0; |
|
|
|
#ifndef ANDROID |
|
for ( ; dj < roiw32; dj += 96, sj += 48) |
|
{ |
|
internal::prefetch(src_row + sj); |
|
|
|
uint8x16x3_t v_src = vld3q_u8(src_row + sj); |
|
uint8x16x2_t v_c0 = vzipq_u8(v_src.val[0], v_src.val[0]); |
|
uint8x16x2_t v_c1 = vzipq_u8(v_src.val[1], v_src.val[1]); |
|
uint8x16x2_t v_c2 = vzipq_u8(v_src.val[2], v_src.val[2]); |
|
|
|
uint8x16x3_t v_dst; |
|
v_dst.val[0] = v_c0.val[0]; |
|
v_dst.val[1] = v_c1.val[0]; |
|
v_dst.val[2] = v_c2.val[0]; |
|
vst3q_u8(dst0_row + dj, v_dst); |
|
vst3q_u8(dst1_row + dj, v_dst); |
|
|
|
v_dst.val[0] = v_c0.val[1]; |
|
v_dst.val[1] = v_c1.val[1]; |
|
v_dst.val[2] = v_c2.val[1]; |
|
vst3q_u8(dst0_row + dj + 48, v_dst); |
|
vst3q_u8(dst1_row + dj + 48, v_dst); |
|
} |
|
#endif |
|
|
|
for ( ; dj < roiw16; dj += 48, sj += 24) |
|
{ |
|
internal::prefetch(src_row + sj); |
|
|
|
uint8x8x3_t v_src = vld3_u8(src_row + sj); |
|
uint8x8x2_t v_c0 = vzip_u8(v_src.val[0], v_src.val[0]); |
|
uint8x8x2_t v_c1 = vzip_u8(v_src.val[1], v_src.val[1]); |
|
uint8x8x2_t v_c2 = vzip_u8(v_src.val[2], v_src.val[2]); |
|
|
|
uint8x16x3_t v_dst; |
|
v_dst.val[0] = vcombine_u8(v_c0.val[0], v_c0.val[1]); |
|
v_dst.val[1] = vcombine_u8(v_c1.val[0], v_c1.val[1]); |
|
v_dst.val[2] = vcombine_u8(v_c2.val[0], v_c2.val[1]); |
|
vst3q_u8(dst0_row + dj, v_dst); |
|
vst3q_u8(dst1_row + dj, v_dst); |
|
} |
|
|
|
for (size_t dwidth = dsize.width * 3; dj < dwidth; dj += 6, sj += 3) |
|
{ |
|
u8 src_val = src_row[sj]; |
|
dst0_row[dj] = dst0_row[dj + 3] = src_val; |
|
dst1_row[dj] = dst1_row[dj + 3] = src_val; |
|
|
|
src_val = src_row[sj + 1]; |
|
dst0_row[dj + 1] = dst0_row[dj + 4] = src_val; |
|
dst1_row[dj + 1] = dst1_row[dj + 4] = src_val; |
|
|
|
src_val = src_row[sj + 2]; |
|
dst0_row[dj + 2] = dst0_row[dj + 5] = src_val; |
|
dst1_row[dj + 2] = dst1_row[dj + 5] = src_val; |
|
} |
|
} |
|
} |
|
else //if ((hr == 4.0f) && (wr == 4.0f)) //the only scale that lasts after isSupported check |
|
{ |
|
#ifndef ANDROID |
|
size_t roiw8 = dsize.width >= 7 ? (dsize.width - 7) * 3 : 0; |
|
#endif |
|
|
|
for (size_t i = 0; i < dsize.height; ++i) |
|
{ |
|
const u8 * src0_row = internal::getRowPtr(srcBase, srcStride, i << 2); |
|
const u8 * src1_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 1); |
|
const u8 * src2_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 2); |
|
const u8 * src3_row = internal::getRowPtr(srcBase, srcStride, (i << 2) + 3); |
|
u8 * dst_row = internal::getRowPtr(dstBase, dstStride, i); |
|
size_t sj = 0, dj = 0; |
|
|
|
#ifndef ANDROID |
|
for ( ; dj < roiw8; dj += 24, sj += 96) |
|
{ |
|
internal::prefetch(src0_row + sj); |
|
internal::prefetch(src1_row + sj); |
|
internal::prefetch(src2_row + sj); |
|
internal::prefetch(src3_row + sj); |
|
|
|
uint8x16x3_t vLane10 = vld3q_u8(src0_row + sj), vLane11 = vld3q_u8(src0_row + sj + 48); |
|
uint8x16x3_t vLane20 = vld3q_u8(src1_row + sj), vLane21 = vld3q_u8(src1_row + sj + 48); |
|
uint8x16x3_t vLane30 = vld3q_u8(src2_row + sj), vLane31 = vld3q_u8(src2_row + sj + 48); |
|
uint8x16x3_t vLane40 = vld3q_u8(src3_row + sj), vLane41 = vld3q_u8(src3_row + sj + 48); |
|
|
|
uint8x8x3_t v_dst; |
|
|
|
// channel 0 |
|
{ |
|
uint16x8_t v_lane0 = vpaddlq_u8(vLane10.val[0]); |
|
uint16x8_t v_lane1 = vpaddlq_u8(vLane20.val[0]); |
|
uint16x8_t v_lane2 = vpaddlq_u8(vLane30.val[0]); |
|
uint16x8_t v_lane3 = vpaddlq_u8(vLane40.val[0]); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane1); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane2); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane3); |
|
|
|
uint16x8_t v_lane0_ = vpaddlq_u8(vLane11.val[0]); |
|
uint16x8_t v_lane1_ = vpaddlq_u8(vLane21.val[0]); |
|
uint16x8_t v_lane2_ = vpaddlq_u8(vLane31.val[0]); |
|
uint16x8_t v_lane3_ = vpaddlq_u8(vLane41.val[0]); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane1_); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane2_); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane3_); |
|
|
|
v_dst.val[0] = areaDownsamplingDivision<opencv_like,4>( |
|
vcombine_u16(vmovn_u32(vpaddlq_u16(v_lane0)), |
|
vmovn_u32(vpaddlq_u16(v_lane0_)))); |
|
} |
|
|
|
// channel 1 |
|
{ |
|
uint16x8_t v_lane0 = vpaddlq_u8(vLane10.val[1]); |
|
uint16x8_t v_lane1 = vpaddlq_u8(vLane20.val[1]); |
|
uint16x8_t v_lane2 = vpaddlq_u8(vLane30.val[1]); |
|
uint16x8_t v_lane3 = vpaddlq_u8(vLane40.val[1]); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane1); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane2); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane3); |
|
|
|
uint16x8_t v_lane0_ = vpaddlq_u8(vLane11.val[1]); |
|
uint16x8_t v_lane1_ = vpaddlq_u8(vLane21.val[1]); |
|
uint16x8_t v_lane2_ = vpaddlq_u8(vLane31.val[1]); |
|
uint16x8_t v_lane3_ = vpaddlq_u8(vLane41.val[1]); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane1_); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane2_); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane3_); |
|
|
|
v_dst.val[1] = areaDownsamplingDivision<opencv_like,4>( |
|
vcombine_u16(vmovn_u32(vpaddlq_u16(v_lane0)), |
|
vmovn_u32(vpaddlq_u16(v_lane0_)))); |
|
} |
|
|
|
// channel 2 |
|
{ |
|
uint16x8_t v_lane0 = vpaddlq_u8(vLane10.val[2]); |
|
uint16x8_t v_lane1 = vpaddlq_u8(vLane20.val[2]); |
|
uint16x8_t v_lane2 = vpaddlq_u8(vLane30.val[2]); |
|
uint16x8_t v_lane3 = vpaddlq_u8(vLane40.val[2]); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane1); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane2); |
|
v_lane0 = vaddq_u16(v_lane0, v_lane3); |
|
|
|
uint16x8_t v_lane0_ = vpaddlq_u8(vLane11.val[2]); |
|
uint16x8_t v_lane1_ = vpaddlq_u8(vLane21.val[2]); |
|
uint16x8_t v_lane2_ = vpaddlq_u8(vLane31.val[2]); |
|
uint16x8_t v_lane3_ = vpaddlq_u8(vLane41.val[2]); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane1_); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane2_); |
|
v_lane0_ = vaddq_u16(v_lane0_, v_lane3_); |
|
|
|
v_dst.val[2] = areaDownsamplingDivision<opencv_like,4>( |
|
vcombine_u16(vmovn_u32(vpaddlq_u16(v_lane0)), |
|
vmovn_u32(vpaddlq_u16(v_lane0_)))); |
|
} |
|
|
|
vst3_u8(dst_row + dj, v_dst); |
|
} |
|
#endif |
|
|
|
for (size_t dwidth = dsize.width * 3; dj < dwidth; dj += 3, sj += 12) |
|
{ |
|
dst_row[dj ] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj ] + src0_row[sj + 3] + |
|
src0_row[sj + 6] + src0_row[sj + 9] + |
|
src1_row[sj ] + src1_row[sj + 3] + |
|
src1_row[sj + 6] + src1_row[sj + 9] + |
|
src2_row[sj ] + src2_row[sj + 3] + |
|
src2_row[sj + 6] + src2_row[sj + 9] + |
|
src3_row[sj ] + src3_row[sj + 3] + |
|
src3_row[sj + 6] + src3_row[sj + 9]); |
|
|
|
dst_row[dj + 1] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj + 1] + src0_row[sj + 4] + |
|
src0_row[sj + 7] + src0_row[sj + 10] + |
|
src1_row[sj + 1] + src1_row[sj + 4] + |
|
src1_row[sj + 7] + src1_row[sj + 10] + |
|
src2_row[sj + 1] + src2_row[sj + 4] + |
|
src2_row[sj + 7] + src2_row[sj + 10] + |
|
src3_row[sj + 1] + src3_row[sj + 4] + |
|
src3_row[sj + 7] + src3_row[sj + 10]); |
|
|
|
dst_row[dj + 2] = areaDownsamplingDivision<opencv_like,4>( |
|
(u16)src0_row[sj + 2] + src0_row[sj + 5] + |
|
src0_row[sj + 8] + src0_row[sj + 11] + |
|
src1_row[sj + 2] + src1_row[sj + 5] + |
|
src1_row[sj + 8] + src1_row[sj + 11] + |
|
src2_row[sj + 2] + src2_row[sj + 5] + |
|
src2_row[sj + 8] + src2_row[sj + 11] + |
|
src3_row[sj + 2] + src3_row[sj + 5] + |
|
src3_row[sj + 8] + src3_row[sj + 11]); |
|
} |
|
} |
|
} |
|
} |
|
#else |
|
(void)dsize; |
|
(void)srcBase; |
|
(void)srcStride; |
|
(void)dstBase; |
|
(void)dstStride; |
|
(void)wr; |
|
(void)hr; |
|
#endif |
|
(void)ssize; |
|
} |
|
|
|
void resizeAreaOpenCV(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr, u32 channels) |
|
{ |
|
resizeAreaRounding<true>(ssize, dsize, srcBase, srcStride, dstBase, dstStride, wr, hr, channels); |
|
} |
|
|
|
void resizeArea(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr, u32 channels) |
|
{ |
|
resizeAreaRounding<false>(ssize, dsize, srcBase, srcStride, dstBase, dstStride, wr, hr, channels); |
|
} |
|
|
|
#ifdef CAROTENE_NEON |
|
|
|
namespace { |
|
|
|
uint8x8_t resizeLinearStep(uint8x16_t vr1, uint8x16_t vr2, |
|
uint8x8_t vlutl, uint8x8_t vluth, |
|
float32x4_t vrw, float32x4_t vcw0, float32x4_t vcw1) |
|
{ |
|
uint8x8_t vr1l = internal::vqtbl1_u8(vr1, vlutl); |
|
uint8x8_t vr1h = internal::vqtbl1_u8(vr1, vluth); |
|
uint8x8_t vr2l = internal::vqtbl1_u8(vr2, vlutl); |
|
uint8x8_t vr2h = internal::vqtbl1_u8(vr2, vluth); |
|
|
|
uint16x8_t v1hw = vmovl_u8(vr1h); |
|
uint16x8_t v2hw = vmovl_u8(vr2h); |
|
|
|
int16x8_t v1df = vreinterpretq_s16_u16(vsubl_u8(vr1l, vr1h)); |
|
int16x8_t v2df = vreinterpretq_s16_u16(vsubl_u8(vr2l, vr2h)); |
|
|
|
float32x4_t v1L = vcvtq_f32_u32(vmovl_u16(vget_low_u16(v1hw))); |
|
float32x4_t v1H = vcvtq_f32_u32(vmovl_u16(vget_high_u16(v1hw))); |
|
float32x4_t v2L = vcvtq_f32_u32(vmovl_u16(vget_low_u16(v2hw))); |
|
float32x4_t v2H = vcvtq_f32_u32(vmovl_u16(vget_high_u16(v2hw))); |
|
|
|
v1L = vmlaq_f32(v1L, vcvtq_f32_s32(vmovl_s16(vget_low_s16(v1df))), vcw0); |
|
v1H = vmlaq_f32(v1H, vcvtq_f32_s32(vmovl_s16(vget_high_s16(v1df))), vcw1); |
|
v2L = vmlaq_f32(v2L, vcvtq_f32_s32(vmovl_s16(vget_low_s16(v2df))), vcw0); |
|
v2H = vmlaq_f32(v2H, vcvtq_f32_s32(vmovl_s16(vget_high_s16(v2df))), vcw1); |
|
|
|
float32x4_t vdiffL = vsubq_f32(v1L, v2L); |
|
float32x4_t vdiffH = vsubq_f32(v1H, v2H); |
|
|
|
float32x4_t vL = vmlaq_f32(v2L, vdiffL, vrw); |
|
float32x4_t vH = vmlaq_f32(v2H, vdiffH, vrw); |
|
uint16x4_t vL_ = vmovn_u32(vcvtq_u32_f32(vL)); |
|
uint16x4_t vH_ = vmovn_u32(vcvtq_u32_f32(vH)); |
|
return vmovn_u16(vcombine_u16(vL_, vH_)); |
|
} |
|
|
|
} // namespace |
|
|
|
namespace { |
|
|
|
void resize_bilinear_rows(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 hr, const u8** gcols, u8* gcweight, u8* buf) |
|
{ |
|
f32 scale_y_offset = 0.5f * hr - 0.5f; |
|
|
|
size_t dst_h8 = dsize.height & ~7; |
|
size_t dst_w8 = dsize.width & ~7; |
|
size_t src_w8 = ssize.width & ~7; |
|
|
|
size_t r = 0; |
|
for (; r < dst_h8; r += 8) |
|
{ |
|
resize8u_xystretch: |
|
const u8* rows[16]; |
|
u8 rweight[8]; |
|
|
|
for (u32 i = 0; i < 8; ++i) |
|
{ |
|
f32 w = (i + r) * hr + scale_y_offset; |
|
ptrdiff_t src_row = floorf(w); |
|
ptrdiff_t src_row2 = src_row + 1; |
|
|
|
rweight[i] = (u8)((src_row2-w) * 128); |
|
|
|
if (src_row < 0) |
|
src_row = 0; |
|
if (src_row2 >= (ptrdiff_t)ssize.height) |
|
src_row2 = ssize.height-1; |
|
|
|
rows[2 * i] = srcBase + src_row * srcStride; |
|
rows[2 * i + 1] = srcBase + src_row2 * srcStride; |
|
} |
|
|
|
uint8x8_t vr0w = vdup_n_u8(rweight[0]); |
|
uint8x8_t vr1w = vdup_n_u8(rweight[1]); |
|
uint8x8_t vr2w = vdup_n_u8(rweight[2]); |
|
uint8x8_t vr3w = vdup_n_u8(rweight[3]); |
|
uint8x8_t vr4w = vdup_n_u8(rweight[4]); |
|
uint8x8_t vr5w = vdup_n_u8(rweight[5]); |
|
uint8x8_t vr6w = vdup_n_u8(rweight[6]); |
|
uint8x8_t vr7w = vdup_n_u8(rweight[7]); |
|
|
|
uint8x8_t vr0w2 = vdup_n_u8(128 - rweight[0]); |
|
uint8x8_t vr1w2 = vdup_n_u8(128 - rweight[1]); |
|
uint8x8_t vr2w2 = vdup_n_u8(128 - rweight[2]); |
|
uint8x8_t vr3w2 = vdup_n_u8(128 - rweight[3]); |
|
uint8x8_t vr4w2 = vdup_n_u8(128 - rweight[4]); |
|
uint8x8_t vr5w2 = vdup_n_u8(128 - rweight[5]); |
|
uint8x8_t vr6w2 = vdup_n_u8(128 - rweight[6]); |
|
uint8x8_t vr7w2 = vdup_n_u8(128 - rweight[7]); |
|
|
|
size_t col = 0; |
|
for(; col < src_w8; col += 8) |
|
{ |
|
internal::prefetch(rows[3] + col); |
|
internal::prefetch(rows[7] + col); |
|
internal::prefetch(rows[11] + col); |
|
internal::prefetch(rows[15] + col); |
|
resize8u_ystretch: |
|
uint8x8_t vsrc0l1 = vld1_u8(rows[0] + col); |
|
uint8x8_t vsrc0l2 = vld1_u8(rows[1] + col); |
|
uint8x8_t vsrc1l1 = vld1_u8(rows[2] + col); |
|
uint8x8_t vsrc1l2 = vld1_u8(rows[3] + col); |
|
|
|
// (l1 * w + l2 * (128 - w) + 64) / 128 |
|
uint16x8_t vdst0l = vmull_u8(vsrc0l1, vr0w); |
|
uint16x8_t vdst1l = vmull_u8(vsrc1l1, vr1w); |
|
|
|
uint8x8_t vsrc2l1 = vld1_u8(rows[4] + col); |
|
uint8x8_t vsrc2l2 = vld1_u8(rows[5] + col); |
|
uint8x8_t vsrc3l1 = vld1_u8(rows[6] + col); |
|
uint8x8_t vsrc3l2 = vld1_u8(rows[7] + col); |
|
|
|
vdst0l = vmlal_u8(vdst0l, vsrc0l2, vr0w2); |
|
vdst1l = vmlal_u8(vdst1l, vsrc1l2, vr1w2); |
|
uint16x8_t vdst2l = vmull_u8(vsrc2l1, vr2w); |
|
uint16x8_t vdst3l = vmull_u8(vsrc3l1, vr3w); |
|
|
|
uint8x8_t vsrc4l1 = vld1_u8(rows[8] + col); |
|
uint8x8_t vsrc4l2 = vld1_u8(rows[9] + col); |
|
uint8x8_t vsrc5l1 = vld1_u8(rows[10] + col); |
|
uint8x8_t vsrc5l2 = vld1_u8(rows[11] + col); |
|
|
|
vdst2l = vmlal_u8(vdst2l, vsrc2l2, vr2w2); |
|
vdst3l = vmlal_u8(vdst3l, vsrc3l2, vr3w2); |
|
uint16x8_t vdst4l = vmull_u8(vsrc4l1, vr4w); |
|
uint16x8_t vdst5l = vmull_u8(vsrc5l1, vr5w); |
|
|
|
uint8x8_t vsrc6l1 = vld1_u8(rows[12] + col); |
|
uint8x8_t vsrc6l2 = vld1_u8(rows[13] + col); |
|
uint8x8_t vsrc7l1 = vld1_u8(rows[14] + col); |
|
uint8x8_t vsrc7l2 = vld1_u8(rows[15] + col); |
|
|
|
uint8x8_t vdst0 = vrshrn_n_u16(vdst0l, 7); |
|
uint8x8_t vdst1 = vrshrn_n_u16(vdst1l, 7); |
|
vdst4l = vmlal_u8(vdst4l, vsrc4l2, vr4w2); |
|
vdst5l = vmlal_u8(vdst5l, vsrc5l2, vr5w2); |
|
uint16x8_t vdst6l = vmull_u8(vsrc6l1, vr6w); |
|
uint16x8_t vdst7l = vmull_u8(vsrc7l1, vr7w); |
|
|
|
uint8x8_t vdst2 = vrshrn_n_u16(vdst2l, 7); |
|
uint8x8_t vdst3 = vrshrn_n_u16(vdst3l, 7); |
|
vdst6l = vmlal_u8(vdst6l, vsrc6l2, vr6w2); |
|
vdst7l = vmlal_u8(vdst7l, vsrc7l2, vr7w2); |
|
|
|
uint8x8_t vdst4 = vrshrn_n_u16(vdst4l, 7); |
|
uint8x8_t vdst5 = vrshrn_n_u16(vdst5l, 7); |
|
uint8x8_t vdst6 = vrshrn_n_u16(vdst6l, 7); |
|
uint8x8_t vdst7 = vrshrn_n_u16(vdst7l, 7); |
|
|
|
// == 8x8 matrix transpose == |
|
|
|
//00 01 02 03 04 05 06 07 d0 |
|
//10 11 12 13 14 15 16 17 d1 |
|
//20 21 22 23 24 25 26 27 d2 |
|
//30 31 32 33 34 35 36 37 d3 |
|
//40 41 42 43 44 45 46 47 d4 |
|
//50 51 52 53 54 55 56 57 d5 |
|
//60 61 62 63 64 65 66 67 d6 |
|
//70 71 72 73 74 75 76 77 d7 |
|
|
|
uint8x8x2_t vdst10t = vtrn_u8(vdst0, vdst1); |
|
uint8x8x2_t vdst32t = vtrn_u8(vdst2, vdst3); |
|
uint8x8x2_t vdst54t = vtrn_u8(vdst4, vdst5); |
|
uint8x8x2_t vdst76t = vtrn_u8(vdst6, vdst7); |
|
|
|
uint8x16_t vd1d0 = vcombine_u8(vdst10t.val[0], vdst10t.val[1]); |
|
uint8x16_t vd3d2 = vcombine_u8(vdst32t.val[0], vdst32t.val[1]); |
|
uint8x16_t vd5d4 = vcombine_u8(vdst54t.val[0], vdst54t.val[1]); |
|
uint8x16_t vd7d6 = vcombine_u8(vdst76t.val[0], vdst76t.val[1]); |
|
|
|
//00 10 02 12 04 14 06 16 d0 |
|
//01 11 03 13 05 15 07 17 d1 |
|
//20 30 22 32 24 34 26 36 d2 |
|
//21 31 23 33 25 35 27 37 d3 |
|
//40 50 42 52 44 54 46 56 d4 |
|
//41 51 43 53 45 55 47 57 d5 |
|
//60 70 62 72 64 74 66 76 d6 |
|
//61 71 63 73 65 75 67 77 d7 |
|
|
|
uint16x8x2_t vq1q0t = vtrnq_u16((uint16x8_t)vd1d0, (uint16x8_t)vd3d2); |
|
uint16x8x2_t vq3q2t = vtrnq_u16((uint16x8_t)vd5d4, (uint16x8_t)vd7d6); |
|
|
|
//00 10 20 30 04 14 24 34 d0 |
|
//01 11 21 31 05 15 25 35 d1 |
|
//02 12 22 32 06 16 26 36 d2 |
|
//03 13 23 33 07 17 27 37 d3 |
|
//40 50 60 70 44 54 64 74 d4 |
|
//41 51 61 71 45 55 65 75 d5 |
|
//42 52 62 72 46 56 66 76 d6 |
|
//43 53 63 73 47 57 67 77 d7 |
|
|
|
uint32x4x2_t vq2q0t = vtrnq_u32((uint32x4_t)vq1q0t.val[0], (uint32x4_t)vq3q2t.val[0]); |
|
uint32x4x2_t vq3q1t = vtrnq_u32((uint32x4_t)vq1q0t.val[1], (uint32x4_t)vq3q2t.val[1]); |
|
|
|
//00 10 20 30 40 50 60 70 d0 |
|
//01 11 21 31 41 51 61 71 d1 |
|
//02 12 22 32 42 52 62 72 d2 |
|
//03 13 23 33 43 53 63 73 d3 |
|
//04 14 24 34 44 54 64 74 d4 |
|
//05 15 25 35 45 55 65 75 d5 |
|
//06 16 26 36 46 56 66 76 d6 |
|
//07 17 27 37 47 57 67 77 d7 |
|
|
|
vst1q_u8(buf + col * 8 + 0, (uint8x16_t)vq2q0t.val[0]); |
|
vst1q_u8(buf + col * 8 + 16, (uint8x16_t)vq3q1t.val[0]); |
|
vst1q_u8(buf + col * 8 + 32, (uint8x16_t)vq2q0t.val[1]); |
|
vst1q_u8(buf + col * 8 + 48, (uint8x16_t)vq3q1t.val[1]); |
|
} |
|
|
|
if (col < ssize.width) |
|
{ |
|
col = ssize.width - 8; |
|
goto resize8u_ystretch; |
|
} |
|
|
|
u8* dst_data = dstBase + r * dstStride; |
|
const u8** cols = gcols; |
|
u8* cweight = gcweight; |
|
|
|
size_t dcol = 0; |
|
for (; dcol < dst_w8; dcol += 8, cols += 16, cweight += 8) |
|
{ |
|
internal::prefetch(cols[0], 64*4); |
|
resize8u_xstretch: |
|
uint8x8_t vc0w = vdup_n_u8(cweight[0]); |
|
uint8x8_t vc1w = vdup_n_u8(cweight[1]); |
|
uint8x8_t vc2w = vdup_n_u8(cweight[2]); |
|
uint8x8_t vc3w = vdup_n_u8(cweight[3]); |
|
uint8x8_t vc4w = vdup_n_u8(cweight[4]); |
|
uint8x8_t vc5w = vdup_n_u8(cweight[5]); |
|
uint8x8_t vc6w = vdup_n_u8(cweight[6]); |
|
uint8x8_t vc7w = vdup_n_u8(cweight[7]); |
|
|
|
uint8x8_t vc0w2 = vdup_n_u8(128 - cweight[0]); |
|
uint8x8_t vc1w2 = vdup_n_u8(128 - cweight[1]); |
|
uint8x8_t vc2w2 = vdup_n_u8(128 - cweight[2]); |
|
uint8x8_t vc3w2 = vdup_n_u8(128 - cweight[3]); |
|
uint8x8_t vc4w2 = vdup_n_u8(128 - cweight[4]); |
|
uint8x8_t vc5w2 = vdup_n_u8(128 - cweight[5]); |
|
uint8x8_t vc6w2 = vdup_n_u8(128 - cweight[6]); |
|
uint8x8_t vc7w2 = vdup_n_u8(128 - cweight[7]); |
|
|
|
uint8x8_t vsrc0l1 = vld1_u8(cols[0]); |
|
uint8x8_t vsrc0l2 = vld1_u8(cols[1]); |
|
uint8x8_t vsrc1l1 = vld1_u8(cols[2]); |
|
uint8x8_t vsrc1l2 = vld1_u8(cols[3]); |
|
uint8x8_t vsrc2l1 = vld1_u8(cols[4]); |
|
uint8x8_t vsrc2l2 = vld1_u8(cols[5]); |
|
uint8x8_t vsrc3l1 = vld1_u8(cols[6]); |
|
uint8x8_t vsrc3l2 = vld1_u8(cols[7]); |
|
uint8x8_t vsrc4l1 = vld1_u8(cols[8]); |
|
uint8x8_t vsrc4l2 = vld1_u8(cols[9]); |
|
uint8x8_t vsrc5l1 = vld1_u8(cols[10]); |
|
uint8x8_t vsrc5l2 = vld1_u8(cols[11]); |
|
uint8x8_t vsrc6l1 = vld1_u8(cols[12]); |
|
uint8x8_t vsrc6l2 = vld1_u8(cols[13]); |
|
uint8x8_t vsrc7l1 = vld1_u8(cols[14]); |
|
uint8x8_t vsrc7l2 = vld1_u8(cols[15]); |
|
|
|
// (l1 * w + l2 * (128 - w) + 64) / 128 |
|
uint16x8_t vdst0l = vmull_u8(vsrc0l1, vc0w); |
|
uint16x8_t vdst1l = vmull_u8(vsrc1l1, vc1w); |
|
uint16x8_t vdst2l = vmull_u8(vsrc2l1, vc2w); |
|
uint16x8_t vdst3l = vmull_u8(vsrc3l1, vc3w); |
|
uint16x8_t vdst4l = vmull_u8(vsrc4l1, vc4w); |
|
uint16x8_t vdst5l = vmull_u8(vsrc5l1, vc5w); |
|
uint16x8_t vdst6l = vmull_u8(vsrc6l1, vc6w); |
|
uint16x8_t vdst7l = vmull_u8(vsrc7l1, vc7w); |
|
|
|
vdst0l = vmlal_u8(vdst0l, vsrc0l2, vc0w2); |
|
vdst1l = vmlal_u8(vdst1l, vsrc1l2, vc1w2); |
|
vdst2l = vmlal_u8(vdst2l, vsrc2l2, vc2w2); |
|
vdst3l = vmlal_u8(vdst3l, vsrc3l2, vc3w2); |
|
vdst4l = vmlal_u8(vdst4l, vsrc4l2, vc4w2); |
|
vdst5l = vmlal_u8(vdst5l, vsrc5l2, vc5w2); |
|
vdst6l = vmlal_u8(vdst6l, vsrc6l2, vc6w2); |
|
vdst7l = vmlal_u8(vdst7l, vsrc7l2, vc7w2); |
|
|
|
uint8x8_t vdst0 = vrshrn_n_u16(vdst0l, 7); |
|
uint8x8_t vdst1 = vrshrn_n_u16(vdst1l, 7); |
|
uint8x8_t vdst2 = vrshrn_n_u16(vdst2l, 7); |
|
uint8x8_t vdst3 = vrshrn_n_u16(vdst3l, 7); |
|
uint8x8_t vdst4 = vrshrn_n_u16(vdst4l, 7); |
|
uint8x8_t vdst5 = vrshrn_n_u16(vdst5l, 7); |
|
uint8x8_t vdst6 = vrshrn_n_u16(vdst6l, 7); |
|
uint8x8_t vdst7 = vrshrn_n_u16(vdst7l, 7); |
|
|
|
// == 8x8 matrix transpose == |
|
uint8x8x2_t vdst10t = vtrn_u8(vdst0, vdst1); |
|
uint8x8x2_t vdst32t = vtrn_u8(vdst2, vdst3); |
|
uint8x8x2_t vdst54t = vtrn_u8(vdst4, vdst5); |
|
uint8x8x2_t vdst76t = vtrn_u8(vdst6, vdst7); |
|
uint8x16_t vd1d0 = vcombine_u8(vdst10t.val[0], vdst10t.val[1]); |
|
uint8x16_t vd3d2 = vcombine_u8(vdst32t.val[0], vdst32t.val[1]); |
|
uint8x16_t vd5d4 = vcombine_u8(vdst54t.val[0], vdst54t.val[1]); |
|
uint8x16_t vd7d6 = vcombine_u8(vdst76t.val[0], vdst76t.val[1]); |
|
uint16x8x2_t vq1q0t = vtrnq_u16((uint16x8_t)vd1d0, (uint16x8_t)vd3d2); |
|
uint16x8x2_t vq3q2t = vtrnq_u16((uint16x8_t)vd5d4, (uint16x8_t)vd7d6); |
|
uint32x4x2_t vq2q0t = vtrnq_u32((uint32x4_t)vq1q0t.val[0], (uint32x4_t)vq3q2t.val[0]); |
|
uint32x4x2_t vq3q1t = vtrnq_u32((uint32x4_t)vq1q0t.val[1], (uint32x4_t)vq3q2t.val[1]); |
|
|
|
//save results |
|
vst1_u8(dst_data + 0 * dstStride + dcol, (uint8x8_t)vget_low_u32(vq2q0t.val[0])); |
|
vst1_u8(dst_data + 1 * dstStride + dcol, (uint8x8_t)vget_high_u32(vq2q0t.val[0])); |
|
vst1_u8(dst_data + 2 * dstStride + dcol, (uint8x8_t)vget_low_u32(vq3q1t.val[0])); |
|
vst1_u8(dst_data + 3 * dstStride + dcol, (uint8x8_t)vget_high_u32(vq3q1t.val[0])); |
|
vst1_u8(dst_data + 4 * dstStride + dcol, (uint8x8_t)vget_low_u32(vq2q0t.val[1])); |
|
vst1_u8(dst_data + 5 * dstStride + dcol, (uint8x8_t)vget_high_u32(vq2q0t.val[1])); |
|
vst1_u8(dst_data + 6 * dstStride + dcol, (uint8x8_t)vget_low_u32(vq3q1t.val[1])); |
|
vst1_u8(dst_data + 7 * dstStride + dcol, (uint8x8_t)vget_high_u32(vq3q1t.val[1])); |
|
} |
|
|
|
if (dcol < dsize.width) |
|
{ |
|
dcol = dsize.width - 8; |
|
cols = gcols + dcol * 2; |
|
cweight = gcweight + dcol; |
|
goto resize8u_xstretch; |
|
} |
|
} |
|
|
|
if (r < dsize.height) |
|
{ |
|
r = dsize.height - 8; |
|
goto resize8u_xystretch; |
|
} |
|
} |
|
|
|
template <int channels> struct resizeLinearInternals; |
|
template <> struct resizeLinearInternals<1> |
|
{ |
|
int32x4_t vc_upd; |
|
int32x4_t vc0; |
|
int32x4_t vcmax; |
|
|
|
inline resizeLinearInternals(int32x4_t & vi, u32 srccols) |
|
{ |
|
vc_upd = vdupq_n_s32(4); |
|
vc0 = vdupq_n_s32(0); |
|
vcmax = vdupq_n_s32(srccols-1); |
|
|
|
s32 tmp0123[] = {0, 1, 2, 3 }; |
|
vi = vld1q_s32(tmp0123); |
|
} |
|
inline void updateIndexes(int32x4_t & vi, int32x4_t & vsrch, int32x4_t & vsrcl) |
|
{ |
|
vsrch = vminq_s32(vsrch, vcmax); |
|
vsrcl = vmaxq_s32(vsrcl, vc0); |
|
vsrcl = vminq_s32(vsrcl, vcmax);//for safe tail |
|
vsrch = vshlq_n_s32(vsrch, 3); |
|
vsrcl = vshlq_n_s32(vsrcl, 3); |
|
vi = vaddq_s32(vi, vc_upd); |
|
} |
|
}; |
|
template <> struct resizeLinearInternals<4> |
|
{ |
|
int32x4_t vc_upd; |
|
int32x4_t vc0; |
|
int32x4_t vcmax; |
|
int32x4_t v0123x8; |
|
|
|
inline resizeLinearInternals(int32x4_t & vi, u32 srccols) |
|
{ |
|
vc_upd = vdupq_n_s32(1); |
|
vc0 = vdupq_n_s32(0); |
|
vcmax = vdupq_n_s32(srccols-1); |
|
s32 tmp0123x8[] = {0, 8, 16, 24}; |
|
v0123x8 = vld1q_s32(tmp0123x8); |
|
|
|
vi = vc0; |
|
} |
|
inline void updateIndexes(int32x4_t & vi, int32x4_t & vsrch, int32x4_t & vsrcl) |
|
{ |
|
vsrch = vminq_s32(vsrch, vcmax); |
|
vsrcl = vmaxq_s32(vsrcl, vc0); |
|
vsrch = vshlq_n_s32(vsrch, 5); |
|
vsrcl = vshlq_n_s32(vsrcl, 5); |
|
vsrch = vaddq_s32(vsrch, v0123x8); |
|
vsrcl = vaddq_s32(vsrcl, v0123x8); |
|
vi = vaddq_s32(vi, vc_upd); |
|
} |
|
}; |
|
|
|
template <int channels> |
|
void resizeLinearOpenCVchan(const Size2D &_ssize, const Size2D &_dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr) |
|
{ |
|
float scale_x_offset = 0.5f * wr - 0.5f; |
|
|
|
Size2D ssize(_ssize.width*channels, _ssize.height); |
|
Size2D dsize(_dsize.width*channels, _dsize.height); |
|
|
|
std::vector<u8> gcweight((dsize.width + 7) & ~7); |
|
std::vector<const u8*> gcols(((dsize.width + 7) & ~7) * 2); |
|
std::vector<u8> buf(((ssize.width + 7) & ~7) * 8); // (8 rows) x (width of src) |
|
|
|
float32x4_t vscale_x = vdupq_n_f32(wr); |
|
float32x4_t vscale_x_offset = vdupq_n_f32(scale_x_offset); |
|
int32x4_t vc1 = vdupq_n_s32(1); |
|
float32x4_t vc128f = vdupq_n_f32(128.0f); |
|
|
|
int32x4_t vi; |
|
resizeLinearInternals<channels> indexes(vi, _ssize.width);//u32 is used to store indexes |
|
//so we could get issues on src image dimensions greater than (2^32-1) |
|
|
|
for (size_t dcol = 0; dcol < dsize.width; dcol += 8) |
|
{ |
|
s32 idx[16]; |
|
|
|
float32x4_t vif = vcvtq_f32_s32(vi); |
|
float32x4_t vw = vmlaq_f32(vscale_x_offset, vscale_x, vif); |
|
int32x4_t vwi = vcvtq_s32_f32(vw); |
|
float32x4_t vwif = vcvtq_f32_s32(vwi); |
|
int32x4_t vmask = (int32x4_t)vcltq_f32(vwif, vw); |
|
int32x4_t vsrch = vsubq_s32(vwi, vmask); |
|
int32x4_t vsrcl = vsubq_s32(vsrch, vc1); |
|
float32x4_t vsrchf = vcvtq_f32_s32(vsrch); |
|
float32x4_t vw2 = vsubq_f32(vsrchf, vw); |
|
|
|
vw2 = vmulq_f32(vw2, vc128f); |
|
uint32x4_t vw32u = vcvtq_u32_f32(vw2); |
|
uint16x4_t vw16ul = vmovn_u32(vw32u); |
|
indexes.updateIndexes(vi, vsrch, vsrcl); |
|
|
|
vst1q_s32(idx + 0, vsrcl); |
|
vst1q_s32(idx + 8, vsrch); |
|
|
|
vif = vcvtq_f32_s32(vi); |
|
vw = vmlaq_f32(vscale_x_offset, vscale_x, vif); |
|
vwi = vcvtq_s32_f32(vw); |
|
vwif = vcvtq_f32_s32(vwi); |
|
vmask = (int32x4_t)vcltq_f32(vwif, vw); |
|
vsrch = vsubq_s32(vwi, vmask); |
|
vsrcl = vsubq_s32(vsrch, vc1); |
|
vsrchf = vcvtq_f32_s32(vsrch); |
|
vw2 = vsubq_f32(vsrchf, vw); |
|
|
|
vw2 = vmulq_f32(vw2, vc128f); |
|
vw32u = vcvtq_u32_f32(vw2); |
|
indexes.updateIndexes(vi, vsrch, vsrcl); |
|
|
|
uint16x4_t vw16uh = vmovn_u32(vw32u); |
|
|
|
vst1q_s32(idx + 4, vsrcl); |
|
vst1q_s32(idx + 12, vsrch); |
|
|
|
uint8x8_t vw8u = vmovn_u16(vcombine_u16(vw16ul, vw16uh)); |
|
|
|
for (u32 i = 0; i < 8; ++i) |
|
{ |
|
gcols[dcol * 2 + i*2] = &buf[idx[i]]; |
|
gcols[dcol * 2 + i*2 + 1] = &buf[idx[i + 8]]; |
|
} |
|
|
|
vst1_u8(&gcweight[dcol], vw8u); |
|
} |
|
|
|
resize_bilinear_rows(ssize, dsize, srcBase, srcStride, dstBase, dstStride, hr, &gcols[0], &gcweight[0], &buf[0]); |
|
} |
|
|
|
void downsample_bilinear_8uc1(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr) |
|
{ |
|
internal::assertSupportedConfiguration(wr <= 2.f && hr <= 2.f); |
|
|
|
enum { SHIFT_BITS = 11 }; |
|
|
|
f32 scale_x_offset = 0.5f * wr - 0.5f; |
|
f32 scale_y_offset = 0.5f * hr - 0.5f; |
|
|
|
std::vector<s32> _buf(dsize.height*(2*(sizeof(ptrdiff_t)/sizeof(s32))+1)+1); |
|
ptrdiff_t* buf = (ptrdiff_t*)&_buf[0]; |
|
s32* buf2 = (s32*)buf+2*(sizeof(ptrdiff_t)/sizeof(s32))*dsize.height; |
|
for(size_t row = 0; row < (size_t)dsize.height; ++row) |
|
{ |
|
f32 r = row * hr + scale_y_offset; |
|
ptrdiff_t src_row = floorf(r); |
|
ptrdiff_t src_row2 = src_row + 1; |
|
|
|
f32 rweight = src_row2 - r; |
|
buf2[row] = floorf(rweight * (1 << SHIFT_BITS) + 0.5f); |
|
buf[0 * dsize.height + row] = std::max<ptrdiff_t>(0, src_row); |
|
buf[1 * dsize.height + row] = std::min((ptrdiff_t)ssize.height-1, src_row2); |
|
} |
|
|
|
#define USE_CORRECT_VERSION 0 |
|
|
|
ptrdiff_t col = 0; |
|
/***********************************************/ |
|
for(; col <= (ptrdiff_t)dsize.width-16; col+=16) |
|
{ |
|
ptrdiff_t col1[16]; |
|
ptrdiff_t col2[16]; |
|
s16 cwi[16]; |
|
|
|
for(s32 k = 0; k < 16; ++k) |
|
{ |
|
f32 c = (col + k) * wr + scale_x_offset; |
|
col1[k] = (ptrdiff_t)c; |
|
col2[k] = col1[k] + 1; |
|
|
|
cwi[k] = (short)floorf((col2[k] - c) * (1 << SHIFT_BITS) + 0.5f); |
|
|
|
if(col1[k] < 0) col1[k] = 0; |
|
if(col2[k] >= (ptrdiff_t)ssize.width) col2[k] = ssize.width-1; |
|
} |
|
|
|
ptrdiff_t x = std::min(col1[0], (ptrdiff_t)ssize.width-16); |
|
ptrdiff_t y = std::min(col1[8], (ptrdiff_t)ssize.width-16); |
|
u8 lutl[16]; |
|
u8 luth[16]; |
|
for(s32 k = 0; k < 8; ++k) |
|
{ |
|
lutl[k] = (u8)(col1[k] - x); |
|
luth[k] = (u8)(col2[k] - x); |
|
lutl[k+8] = (u8)(col1[k+8] - y); |
|
luth[k+8] = (u8)(col2[k+8] - y); |
|
} |
|
|
|
uint8x8_t vlutl = vld1_u8(lutl); |
|
uint8x8_t vluth = vld1_u8(luth); |
|
int16x8_t vcw = vld1q_s16(cwi); |
|
|
|
uint8x8_t vlutl_ = vld1_u8(lutl+8); |
|
uint8x8_t vluth_ = vld1_u8(luth+8); |
|
int16x8_t vcw_ = vld1q_s16(cwi+8); |
|
|
|
for(ptrdiff_t row = 0; row < (ptrdiff_t)dsize.height; ++row) |
|
{ |
|
#if USE_CORRECT_VERSION |
|
int32x4_t vrw = vdupq_n_s32(buf2[row]); |
|
#else |
|
int16x8_t vrw = vdupq_n_s16((int16_t)buf2[row]); |
|
int16x8_t vrW = vdupq_n_s16((int16_t)((1 << SHIFT_BITS) - buf2[row])); |
|
#endif |
|
|
|
internal::prefetch(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + x, 2*srcStride); |
|
internal::prefetch(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + x, 3*srcStride); |
|
|
|
{ |
|
union { uint8x16_t v; uint8x8x2_t w; } vr1 = { vld1q_u8(internal::getRowPtr(srcBase, srcStride, buf[0*dsize.height + row]) + x) }; |
|
union { uint8x16_t v; uint8x8x2_t w; } vr2 = { vld1q_u8(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + x) }; |
|
|
|
uint8x8_t vr1l = vtbl2_u8(vr1.w, vlutl); |
|
uint8x8_t vr1h = vtbl2_u8(vr1.w, vluth); |
|
uint8x8_t vr2l = vtbl2_u8(vr2.w, vlutl); |
|
uint8x8_t vr2h = vtbl2_u8(vr2.w, vluth); |
|
|
|
uint16x8_t v1hw = vmovl_u8(vr1h); |
|
uint16x8_t v2hw = vmovl_u8(vr2h); |
|
|
|
int16x8_t v1df = vreinterpretq_s16_u16(vsubl_u8(vr1l, vr1h)); |
|
int16x8_t v2df = vreinterpretq_s16_u16(vsubl_u8(vr2l, vr2h)); |
|
|
|
int32x4_t v1L = vreinterpretq_s32_u32(vshll_n_u16(vget_low_u16(v1hw), SHIFT_BITS)); |
|
int32x4_t v1H = vreinterpretq_s32_u32(vshll_n_u16(vget_high_u16(v1hw), SHIFT_BITS)); |
|
int32x4_t v2L = vreinterpretq_s32_u32(vshll_n_u16(vget_low_u16(v2hw), SHIFT_BITS)); |
|
int32x4_t v2H = vreinterpretq_s32_u32(vshll_n_u16(vget_high_u16(v2hw), SHIFT_BITS)); |
|
|
|
v1L = vmlal_s16(v1L, vget_low_s16(v1df), vget_low_s16(vcw)); |
|
v1H = vmlal_s16(v1H, vget_high_s16(v1df), vget_high_s16(vcw)); |
|
v2L = vmlal_s16(v2L, vget_low_s16(v2df), vget_low_s16(vcw)); |
|
v2H = vmlal_s16(v2H, vget_high_s16(v2df), vget_high_s16(vcw)); |
|
|
|
#if USE_CORRECT_VERSION |
|
/* correct version */ |
|
int32x4_t vL = vshlq_n_s32(v2L, SHIFT_BITS); |
|
int32x4_t vH = vshlq_n_s32(v2H, SHIFT_BITS); |
|
int32x4_t vdiffL = vsubq_s32(v1L, v2L); |
|
int32x4_t vdiffH = vsubq_s32(v1H, v2H); |
|
|
|
vL = vmlaq_s32(vL, vdiffL, vrw); |
|
vH = vmlaq_s32(vH, vdiffH, vrw); |
|
uint16x4_t vL_ = vqrshrun_n_s32(vL, 2*SHIFT_BITS - 8); |
|
uint16x4_t vH_ = vqrshrun_n_s32(vH, 2*SHIFT_BITS - 8); |
|
uint8x8_t vres = vrshrn_n_u16(vcombine_u16(vL_, vH_), 8); |
|
vst1_u8(internal::getRowPtr(dstBase, dstStride, row) + col, vres); |
|
#else |
|
/* ugly version matching to OpenCV's SSE optimization */ |
|
int16x4_t v1Ls = vshrn_n_s32(v1L, 4); |
|
int16x4_t v1Hs = vshrn_n_s32(v1H, 4); |
|
int16x4_t v2Ls = vshrn_n_s32(v2L, 4); |
|
int16x4_t v2Hs = vshrn_n_s32(v2H, 4); |
|
|
|
int16x8_t v1s = vqdmulhq_s16(vcombine_s16(v1Ls, v1Hs), vrw); |
|
int16x8_t v2s = vqdmulhq_s16(vcombine_s16(v2Ls, v2Hs), vrW); |
|
|
|
int16x8_t vsum = vaddq_s16(vshrq_n_s16(v1s,1), vshrq_n_s16(v2s,1)); |
|
uint8x8_t vres = vqrshrun_n_s16(vsum, 2); |
|
|
|
vst1_u8(internal::getRowPtr(dstBase, dstStride, row) + col, vres); |
|
#endif |
|
} |
|
|
|
{ |
|
union { uint8x16_t v; uint8x8x2_t w; } vr1 = { vld1q_u8(internal::getRowPtr(srcBase, srcStride, buf[0*dsize.height + row]) + y) }; |
|
union { uint8x16_t v; uint8x8x2_t w; } vr2 = { vld1q_u8(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + y) }; |
|
|
|
uint8x8_t vr1l = vtbl2_u8(vr1.w, vlutl_); |
|
uint8x8_t vr1h = vtbl2_u8(vr1.w, vluth_); |
|
uint8x8_t vr2l = vtbl2_u8(vr2.w, vlutl_); |
|
uint8x8_t vr2h = vtbl2_u8(vr2.w, vluth_); |
|
|
|
uint16x8_t v1hw = vmovl_u8(vr1h); |
|
uint16x8_t v2hw = vmovl_u8(vr2h); |
|
|
|
int16x8_t v1df = vreinterpretq_s16_u16(vsubl_u8(vr1l, vr1h)); |
|
int16x8_t v2df = vreinterpretq_s16_u16(vsubl_u8(vr2l, vr2h)); |
|
|
|
int32x4_t v1L = vreinterpretq_s32_u32(vshll_n_u16(vget_low_u16(v1hw), SHIFT_BITS)); |
|
int32x4_t v1H = vreinterpretq_s32_u32(vshll_n_u16(vget_high_u16(v1hw), SHIFT_BITS)); |
|
int32x4_t v2L = vreinterpretq_s32_u32(vshll_n_u16(vget_low_u16(v2hw), SHIFT_BITS)); |
|
int32x4_t v2H = vreinterpretq_s32_u32(vshll_n_u16(vget_high_u16(v2hw), SHIFT_BITS)); |
|
|
|
v1L = vmlal_s16(v1L, vget_low_s16(v1df), vget_low_s16(vcw_)); |
|
v1H = vmlal_s16(v1H, vget_high_s16(v1df), vget_high_s16(vcw_)); |
|
v2L = vmlal_s16(v2L, vget_low_s16(v2df), vget_low_s16(vcw_)); |
|
v2H = vmlal_s16(v2H, vget_high_s16(v2df), vget_high_s16(vcw_)); |
|
|
|
#if USE_CORRECT_VERSION |
|
/* correct version */ |
|
int32x4_t vL = vshlq_n_s32(v2L, SHIFT_BITS); |
|
int32x4_t vH = vshlq_n_s32(v2H, SHIFT_BITS); |
|
int32x4_t vdiffL = vsubq_s32(v1L, v2L); |
|
int32x4_t vdiffH = vsubq_s32(v1H, v2H); |
|
|
|
vL = vmlaq_s32(vL, vdiffL, vrw); |
|
vH = vmlaq_s32(vH, vdiffH, vrw); |
|
uint16x4_t vL_ = vqrshrun_n_s32(vL, 2*SHIFT_BITS - 8); |
|
uint16x4_t vH_ = vqrshrun_n_s32(vH, 2*SHIFT_BITS - 8); |
|
uint8x8_t vres = vrshrn_n_u16(vcombine_u16(vL_, vH_), 8); |
|
vst1_u8(internal::getRowPtr(dstBase, dstStride, row) + col + 8, vres); |
|
#else |
|
/* ugly version matching to OpenCV's SSE optimization */ |
|
int16x4_t v1Ls = vshrn_n_s32(v1L, 4); |
|
int16x4_t v1Hs = vshrn_n_s32(v1H, 4); |
|
int16x4_t v2Ls = vshrn_n_s32(v2L, 4); |
|
int16x4_t v2Hs = vshrn_n_s32(v2H, 4); |
|
|
|
int16x8_t v1s = vqdmulhq_s16(vcombine_s16(v1Ls, v1Hs), vrw); |
|
int16x8_t v2s = vqdmulhq_s16(vcombine_s16(v2Ls, v2Hs), vrW); |
|
|
|
int16x8_t vsum = vaddq_s16(vshrq_n_s16(v1s,1), vshrq_n_s16(v2s,1)); |
|
uint8x8_t vres = vqrshrun_n_s16(vsum, 2); |
|
|
|
vst1_u8(internal::getRowPtr(dstBase, dstStride, row) + col + 8, vres); |
|
#endif |
|
} |
|
} |
|
} |
|
/***********************************************/ |
|
for(; col <= (ptrdiff_t)dsize.width-8; col+=8) |
|
{ |
|
downsample_bilinear_8uc1_col_loop8: |
|
ptrdiff_t col1[8]; |
|
ptrdiff_t col2[8]; |
|
s16 cwi[8]; |
|
|
|
for(s32 k = 0; k < 8; ++k) |
|
{ |
|
f32 c = (col + k) * wr + scale_x_offset; |
|
col1[k] = (ptrdiff_t)c; |
|
col2[k] = col1[k] + 1; |
|
|
|
cwi[k] = (s16)floorf((col2[k] - c) * (1 << SHIFT_BITS) + 0.5f); |
|
|
|
if(col1[k] < 0) col1[k] = 0; |
|
if(col2[k] >= (ptrdiff_t)ssize.width) col2[k] = (ptrdiff_t)ssize.width-1; |
|
} |
|
|
|
ptrdiff_t x = std::min(col1[0], (ptrdiff_t)ssize.width-16); |
|
u8 lutl[8]; |
|
u8 luth[8]; |
|
for(s32 k = 0; k < 8; ++k) |
|
{ |
|
lutl[k] = (u8)(col1[k] - x); |
|
luth[k] = (u8)(col2[k] - x); |
|
} |
|
|
|
uint8x8_t vlutl = vld1_u8(lutl); |
|
uint8x8_t vluth = vld1_u8(luth); |
|
int16x8_t vcw = vld1q_s16(cwi); |
|
|
|
for(ptrdiff_t row = 0; row < (ptrdiff_t)dsize.height; ++row) |
|
{ |
|
#if USE_CORRECT_VERSION |
|
int32x4_t vrw = vdupq_n_s32(buf2[row]); |
|
#else |
|
int16x8_t vrw = vdupq_n_s16((int16_t)buf2[row]); |
|
int16x8_t vrW = vdupq_n_s16((int16_t)((1 << SHIFT_BITS) - buf2[row])); |
|
#endif |
|
|
|
internal::prefetch(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + x, 2*srcStride); |
|
internal::prefetch(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + x, 3*srcStride); |
|
|
|
union { uint8x16_t v; uint8x8x2_t w; } vr1 = { vld1q_u8(internal::getRowPtr(srcBase, srcStride, buf[0*dsize.height + row]) + x) }; |
|
union { uint8x16_t v; uint8x8x2_t w; } vr2 = { vld1q_u8(internal::getRowPtr(srcBase, srcStride, buf[1*dsize.height + row]) + x) }; |
|
|
|
uint8x8_t vr1l = vtbl2_u8(vr1.w, vlutl); |
|
uint8x8_t vr1h = vtbl2_u8(vr1.w, vluth); |
|
uint8x8_t vr2l = vtbl2_u8(vr2.w, vlutl); |
|
uint8x8_t vr2h = vtbl2_u8(vr2.w, vluth); |
|
|
|
uint16x8_t v1hw = vmovl_u8(vr1h); |
|
uint16x8_t v2hw = vmovl_u8(vr2h); |
|
|
|
int16x8_t v1df = vreinterpretq_s16_u16(vsubl_u8(vr1l, vr1h)); |
|
int16x8_t v2df = vreinterpretq_s16_u16(vsubl_u8(vr2l, vr2h)); |
|
|
|
int32x4_t v1L = vreinterpretq_s32_u32(vshll_n_u16(vget_low_u16(v1hw), SHIFT_BITS)); |
|
int32x4_t v1H = vreinterpretq_s32_u32(vshll_n_u16(vget_high_u16(v1hw), SHIFT_BITS)); |
|
int32x4_t v2L = vreinterpretq_s32_u32(vshll_n_u16(vget_low_u16(v2hw), SHIFT_BITS)); |
|
int32x4_t v2H = vreinterpretq_s32_u32(vshll_n_u16(vget_high_u16(v2hw), SHIFT_BITS)); |
|
|
|
v1L = vmlal_s16(v1L, vget_low_s16(v1df), vget_low_s16(vcw)); |
|
v1H = vmlal_s16(v1H, vget_high_s16(v1df), vget_high_s16(vcw)); |
|
v2L = vmlal_s16(v2L, vget_low_s16(v2df), vget_low_s16(vcw)); |
|
v2H = vmlal_s16(v2H, vget_high_s16(v2df), vget_high_s16(vcw)); |
|
|
|
#if USE_CORRECT_VERSION |
|
/* correct version */ |
|
int32x4_t vL = vshlq_n_s32(v2L, SHIFT_BITS); |
|
int32x4_t vH = vshlq_n_s32(v2H, SHIFT_BITS); |
|
int32x4_t vdiffL = vsubq_s32(v1L, v2L); |
|
int32x4_t vdiffH = vsubq_s32(v1H, v2H); |
|
|
|
vL = vmlaq_s32(vL, vdiffL, vrw); |
|
vH = vmlaq_s32(vH, vdiffH, vrw); |
|
uint16x4_t vL_ = vqrshrun_n_s32(vL, 2*SHIFT_BITS - 8); |
|
uint16x4_t vH_ = vqrshrun_n_s32(vH, 2*SHIFT_BITS - 8); |
|
uint8x8_t vres = vrshrn_n_u16(vcombine_u16(vL_, vH_), 8); |
|
vst1_u8(internal::getRowPtr(dstBase, dstStride, row) + col, vres); |
|
#else |
|
/* ugly version matching to OpenCV's SSE optimization */ |
|
int16x4_t v1Ls = vshrn_n_s32(v1L, 4); |
|
int16x4_t v1Hs = vshrn_n_s32(v1H, 4); |
|
int16x4_t v2Ls = vshrn_n_s32(v2L, 4); |
|
int16x4_t v2Hs = vshrn_n_s32(v2H, 4); |
|
|
|
int16x8_t v1s = vqdmulhq_s16(vcombine_s16(v1Ls, v1Hs), vrw); |
|
int16x8_t v2s = vqdmulhq_s16(vcombine_s16(v2Ls, v2Hs), vrW); |
|
|
|
int16x8_t vsum = vaddq_s16(vshrq_n_s16(v1s,1), vshrq_n_s16(v2s,1)); |
|
uint8x8_t vres = vqrshrun_n_s16(vsum, 2); |
|
|
|
vst1_u8(internal::getRowPtr(dstBase, dstStride, row) + col, vres); |
|
#endif |
|
} |
|
} |
|
if (col < (ptrdiff_t)dsize.width) |
|
{ |
|
col = dsize.width - 8; |
|
goto downsample_bilinear_8uc1_col_loop8; |
|
} |
|
} |
|
|
|
} // namespace |
|
|
|
#endif |
|
|
|
void resizeLinearOpenCV(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr, u32 channels) |
|
{ |
|
internal::assertSupportedConfiguration(wr > 0 && hr > 0 && |
|
(dsize.width - 0.5) * wr - 0.5 < ssize.width && |
|
(dsize.height - 0.5) * hr - 0.5 < ssize.height && // Ensure we have enough source data |
|
(dsize.width + 0.5) * wr + 0.5 >= ssize.width && |
|
(dsize.height + 0.5) * hr + 0.5 >= ssize.height && // Ensure source isn't too big |
|
isResizeLinearOpenCVSupported(ssize, dsize, channels)); |
|
#ifdef CAROTENE_NEON |
|
if(1 == channels) |
|
{ |
|
if (wr <= 1.f && hr <= 1.f) |
|
resizeLinearOpenCVchan<1>(ssize, dsize, srcBase, srcStride, dstBase, dstStride, wr, hr); |
|
else if (wr <= 2.0f && hr <= 2.0f && ssize.width >= 16) |
|
downsample_bilinear_8uc1(ssize, dsize, srcBase, srcStride, dstBase, dstStride, wr, hr); |
|
else |
|
resizeLinearOpenCVchan<1>(ssize, dsize, srcBase, srcStride, dstBase, dstStride, wr, hr); |
|
} |
|
else if(4 == channels) |
|
resizeLinearOpenCVchan<4>(ssize, dsize, srcBase, srcStride, dstBase, dstStride, wr, hr); |
|
#else |
|
(void)ssize; |
|
(void)dsize; |
|
(void)srcBase; |
|
(void)srcStride; |
|
(void)dstBase; |
|
(void)dstStride; |
|
(void)wr; |
|
(void)hr; |
|
(void)channels; |
|
#endif |
|
} |
|
|
|
void resizeLinear(const Size2D &ssize, const Size2D &dsize, |
|
const u8 * srcBase, ptrdiff_t srcStride, |
|
u8 * dstBase, ptrdiff_t dstStride, |
|
f32 wr, f32 hr, u32 channels) |
|
{ |
|
internal::assertSupportedConfiguration(wr > 0 && hr > 0 && |
|
(dsize.width - 0.5) * wr - 0.5 < ssize.width && |
|
(dsize.height - 0.5) * hr - 0.5 < ssize.height && // Ensure we have enough source data |
|
(dsize.width + 0.5) * wr + 0.5 >= ssize.width && |
|
(dsize.height + 0.5) * hr + 0.5 >= ssize.height && // Ensure source isn't too big |
|
isResizeLinearSupported(ssize, dsize, |
|
wr, hr, channels)); |
|
#ifdef CAROTENE_NEON |
|
f32 scale_x = wr; |
|
f32 scale_x_offset = 0.5f * scale_x - 0.5f; |
|
f32 scale_y = hr; |
|
f32 scale_y_offset = 0.5f * scale_y - 0.5f; |
|
|
|
std::vector<ptrdiff_t> _buf(dsize.height * 3 + 1); |
|
std::vector<f32> coeff(dsize.height); |
|
ptrdiff_t * buf = &_buf[0]; |
|
|
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
f32 r = row * scale_y + scale_y_offset; |
|
ptrdiff_t src_row = floorf(r); |
|
ptrdiff_t src_row2 = src_row + 1; |
|
|
|
f32 rweight = src_row2 - r; |
|
buf[0 * dsize.height + row] = std::max<ptrdiff_t>(0, src_row); |
|
buf[1 * dsize.height + row] = std::min<ptrdiff_t>(ssize.height - 1, src_row2); |
|
coeff[row] = rweight; |
|
} |
|
|
|
size_t col = 0; |
|
for ( ; col + 16 <= dsize.width; col += 16) |
|
{ |
|
ptrdiff_t col1[16], col2[16]; |
|
f32 cwi[16]; |
|
|
|
for(s32 k = 0; k < 16; ++k) |
|
{ |
|
f32 c = (col + k) * scale_x + scale_x_offset; |
|
col1[k] = floorf(c); |
|
col2[k] = col1[k] + 1; |
|
|
|
cwi[k] = col2[k] - c; |
|
|
|
if (col1[k] < 0) |
|
col1[k] = 0; |
|
if (col2[k] >= (ptrdiff_t)ssize.width) |
|
col2[k] = ssize.width - 1; |
|
} |
|
|
|
ptrdiff_t x = std::min<ptrdiff_t>(col1[0], ssize.width - 16); |
|
ptrdiff_t y = std::min<ptrdiff_t>(col1[8], ssize.width - 16); |
|
u8 lutl[16], luth[16]; |
|
|
|
for (s32 k = 0; k < 8; ++k) |
|
{ |
|
lutl[k] = (u8)(col1[k] - x); |
|
luth[k] = (u8)(col2[k] - x); |
|
lutl[k + 8] = (u8)(col1[k + 8] - y); |
|
luth[k + 8] = (u8)(col2[k + 8] - y); |
|
} |
|
|
|
uint8x8_t vlutl = vld1_u8(lutl); |
|
uint8x8_t vluth = vld1_u8(luth); |
|
float32x4_t vcw0 = vld1q_f32(cwi); |
|
float32x4_t vcw1 = vld1q_f32(cwi + 4); |
|
|
|
uint8x8_t vlutl_ = vld1_u8(lutl + 8); |
|
uint8x8_t vluth_ = vld1_u8(luth + 8); |
|
float32x4_t vcw0_ = vld1q_f32(cwi + 8); |
|
float32x4_t vcw1_ = vld1q_f32(cwi + 12); |
|
|
|
if (channels == 1) |
|
{ |
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
float32x4_t vrw = vdupq_n_f32(coeff[row]); |
|
|
|
const u8 * srow0 = internal::getRowPtr(srcBase, srcStride, buf[0 * dsize.height + row]); |
|
const u8 * srow1 = internal::getRowPtr(srcBase, srcStride, buf[1 * dsize.height + row]); |
|
u8 * drow = internal::getRowPtr(dstBase, dstStride, row); |
|
|
|
internal::prefetch(srow0 + x + 2 * srcStride); |
|
internal::prefetch(srow1 + x + 2 * srcStride); |
|
|
|
uint8x8_t vres0 = resizeLinearStep(vld1q_u8(srow0 + x), vld1q_u8(srow1 + x), |
|
vlutl, vluth, |
|
vrw, vcw0, vcw1); |
|
|
|
uint8x8_t vres1 = resizeLinearStep(vld1q_u8(srow0 + y), vld1q_u8(srow1 + y), |
|
vlutl_, vluth_, |
|
vrw, vcw0_, vcw1_); |
|
|
|
vst1q_u8(drow + col, vcombine_u8(vres0, vres1)); |
|
} |
|
} |
|
else if (channels == 3) |
|
{ |
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
float32x4_t vrw = vdupq_n_f32(coeff[row]); |
|
|
|
const u8 * srow0 = internal::getRowPtr(srcBase, srcStride, buf[0 * dsize.height + row]); |
|
const u8 * srow1 = internal::getRowPtr(srcBase, srcStride, buf[1 * dsize.height + row]); |
|
u8 * drow = internal::getRowPtr(dstBase, dstStride, row); |
|
|
|
internal::prefetch(srow0 + x + 2 * srcStride); |
|
internal::prefetch(srow1 + x + 2 * srcStride); |
|
|
|
uint8x16x3_t v_src10 = vld3q_u8(srow0 + (x * 3)); |
|
uint8x16x3_t v_src20 = vld3q_u8(srow1 + (x * 3)); |
|
|
|
uint8x16x3_t v_src11 = vld3q_u8(srow0 + (y * 3)); |
|
uint8x16x3_t v_src21 = vld3q_u8(srow1 + (y * 3)); |
|
|
|
uint8x16x3_t v_dst; |
|
|
|
v_dst.val[0] = vcombine_u8(resizeLinearStep(v_src10.val[0], v_src20.val[0], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[0], v_src21.val[0], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
v_dst.val[1] = vcombine_u8(resizeLinearStep(v_src10.val[1], v_src20.val[1], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[1], v_src21.val[1], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
v_dst.val[2] = vcombine_u8(resizeLinearStep(v_src10.val[2], v_src20.val[2], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[2], v_src21.val[2], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
|
|
vst3q_u8(drow + (col * 3), v_dst); |
|
} |
|
} |
|
else if (channels == 4) |
|
{ |
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
float32x4_t vrw = vdupq_n_f32(coeff[row]); |
|
|
|
const u8 * srow0 = internal::getRowPtr(srcBase, srcStride, buf[0 * dsize.height + row]); |
|
const u8 * srow1 = internal::getRowPtr(srcBase, srcStride, buf[1 * dsize.height + row]); |
|
u8 * drow = internal::getRowPtr(dstBase, dstStride, row); |
|
|
|
internal::prefetch(srow0 + x + 2 * srcStride); |
|
internal::prefetch(srow1 + x + 2 * srcStride); |
|
|
|
uint8x16x4_t v_src10 = vld4q_u8(srow0 + (x << 2)); |
|
uint8x16x4_t v_src20 = vld4q_u8(srow1 + (x << 2)); |
|
|
|
uint8x16x4_t v_src11 = vld4q_u8(srow0 + (y << 2)); |
|
uint8x16x4_t v_src21 = vld4q_u8(srow1 + (y << 2)); |
|
|
|
uint8x16x4_t v_dst; |
|
|
|
v_dst.val[0] = vcombine_u8(resizeLinearStep(v_src10.val[0], v_src20.val[0], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[0], v_src21.val[0], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
v_dst.val[1] = vcombine_u8(resizeLinearStep(v_src10.val[1], v_src20.val[1], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[1], v_src21.val[1], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
v_dst.val[2] = vcombine_u8(resizeLinearStep(v_src10.val[2], v_src20.val[2], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[2], v_src21.val[2], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
v_dst.val[3] = vcombine_u8(resizeLinearStep(v_src10.val[3], v_src20.val[3], vlutl, vluth, vrw, vcw0, vcw1), |
|
resizeLinearStep(v_src11.val[3], v_src21.val[3], vlutl_, vluth_, vrw, vcw0_, vcw1_)); |
|
|
|
vst4q_u8(drow + (col << 2), v_dst); |
|
} |
|
} |
|
} |
|
|
|
for ( ; col + 8 <= dsize.width; col += 8) |
|
{ |
|
downsample_bilinear_8uc1_col_loop8: |
|
ptrdiff_t col1[8], col2[8]; |
|
f32 cwi[8]; |
|
|
|
for (s32 k = 0; k < 8; ++k) |
|
{ |
|
f32 c = (col + k) * scale_x + scale_x_offset; |
|
col1[k] = floorf(c); |
|
col2[k] = col1[k] + 1; |
|
|
|
cwi[k] = col2[k] - c; |
|
|
|
if (col1[k] < 0) |
|
col1[k] = 0; |
|
if (col2[k] >= (ptrdiff_t)ssize.width) |
|
col2[k] = ssize.width - 1; |
|
} |
|
|
|
ptrdiff_t x = std::min<ptrdiff_t>(col1[0], ssize.width - 16); |
|
u8 lutl[8], luth[8]; |
|
for (s32 k = 0; k < 8; ++k) |
|
{ |
|
lutl[k] = (u8)(col1[k] - x); |
|
luth[k] = (u8)(col2[k] - x); |
|
} |
|
|
|
uint8x8_t vlutl = vld1_u8(lutl); |
|
uint8x8_t vluth = vld1_u8(luth); |
|
float32x4_t vcw0 = vld1q_f32(cwi); |
|
float32x4_t vcw1 = vld1q_f32(cwi + 4); |
|
|
|
if (channels == 1) |
|
{ |
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
float32x4_t vrw = vdupq_n_f32(coeff[row]); |
|
|
|
const u8 * srow0 = internal::getRowPtr(srcBase, srcStride, buf[0 * dsize.height + row]); |
|
const u8 * srow1 = internal::getRowPtr(srcBase, srcStride, buf[1 * dsize.height + row]); |
|
u8 * drow = internal::getRowPtr(dstBase, dstStride, row); |
|
|
|
internal::prefetch(srow0 + x + 2 * srcStride); |
|
internal::prefetch(srow1 + x + 2 * srcStride); |
|
|
|
uint8x8_t vres = resizeLinearStep(vld1q_u8(srow0 + x), vld1q_u8(srow1 + x), |
|
vlutl, vluth, |
|
vrw, vcw0, vcw1); |
|
vst1_u8(drow + col, vres); |
|
} |
|
} |
|
else if (channels == 3) |
|
{ |
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
float32x4_t vrw = vdupq_n_f32(coeff[row]); |
|
|
|
const u8 * srow0 = internal::getRowPtr(srcBase, srcStride, buf[0 * dsize.height + row]); |
|
const u8 * srow1 = internal::getRowPtr(srcBase, srcStride, buf[1 * dsize.height + row]); |
|
u8 * drow = internal::getRowPtr(dstBase, dstStride, row); |
|
|
|
internal::prefetch(srow0 + x + 2 * srcStride); |
|
internal::prefetch(srow1 + x + 2 * srcStride); |
|
|
|
uint8x16x3_t v_src1 = vld3q_u8(srow0 + (x * 3)); |
|
uint8x16x3_t v_src2 = vld3q_u8(srow1 + (x * 3)); |
|
|
|
uint8x8x3_t v_dst; |
|
|
|
v_dst.val[0] = resizeLinearStep(v_src1.val[0], v_src2.val[0], vlutl, vluth, vrw, vcw0, vcw1); |
|
v_dst.val[1] = resizeLinearStep(v_src1.val[1], v_src2.val[1], vlutl, vluth, vrw, vcw0, vcw1); |
|
v_dst.val[2] = resizeLinearStep(v_src1.val[2], v_src2.val[2], vlutl, vluth, vrw, vcw0, vcw1); |
|
|
|
vst3_u8(drow + (col * 3), v_dst); |
|
} |
|
} |
|
else if (channels == 4) |
|
{ |
|
for (size_t row = 0; row < dsize.height; ++row) |
|
{ |
|
float32x4_t vrw = vdupq_n_f32(coeff[row]); |
|
|
|
const u8 * srow0 = internal::getRowPtr(srcBase, srcStride, buf[0 * dsize.height + row]); |
|
const u8 * srow1 = internal::getRowPtr(srcBase, srcStride, buf[1 * dsize.height + row]); |
|
u8 * drow = internal::getRowPtr(dstBase, dstStride, row); |
|
|
|
internal::prefetch(srow0 + x + 2 * srcStride); |
|
internal::prefetch(srow1 + x + 2 * srcStride); |
|
|
|
uint8x16x4_t v_src1 = vld4q_u8(srow0 + (x << 2)); |
|
uint8x16x4_t v_src2 = vld4q_u8(srow1 + (x << 2)); |
|
|
|
uint8x8x4_t v_dst; |
|
|
|
v_dst.val[0] = resizeLinearStep(v_src1.val[0], v_src2.val[0], vlutl, vluth, vrw, vcw0, vcw1); |
|
v_dst.val[1] = resizeLinearStep(v_src1.val[1], v_src2.val[1], vlutl, vluth, vrw, vcw0, vcw1); |
|
v_dst.val[2] = resizeLinearStep(v_src1.val[2], v_src2.val[2], vlutl, vluth, vrw, vcw0, vcw1); |
|
v_dst.val[3] = resizeLinearStep(v_src1.val[3], v_src2.val[3], vlutl, vluth, vrw, vcw0, vcw1); |
|
|
|
vst4_u8(drow + (col << 2), v_dst); |
|
} |
|
} |
|
} |
|
|
|
if (col < dsize.width) |
|
{ |
|
col = dsize.width - 8; |
|
goto downsample_bilinear_8uc1_col_loop8; |
|
} |
|
|
|
#else |
|
(void)ssize; |
|
(void)dsize; |
|
(void)srcBase; |
|
(void)srcStride; |
|
(void)dstBase; |
|
(void)dstStride; |
|
(void)wr; |
|
(void)hr; |
|
(void)channels; |
|
#endif |
|
} |
|
|
|
} // namespace CAROTENE_NS
|
|
|