mirror of
https://github.com/opencv/opencv.git
synced 2026-09-25 04:09:57 +03:00
According to https://github.com/opencv/opencv/wiki/OpenCV-4-to-5-migration#1-build-requirements are not supported: - GCC < 7 - clang < 9 - MSVC < 2017 (19.14)
1562 lines
67 KiB
C++
1562 lines
67 KiB
C++
// This file is part of OpenCV project.
|
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
|
// of this distribution and at http://opencv.org/license.html.
|
|
|
|
// Copyright (C) 2025, BigVision LLC, all rights reserved.
|
|
// Third party copyrights are property of their respective owners.
|
|
#include "../precomp.hpp"
|
|
#include "layers_common.hpp"
|
|
#include "../op_cuda.hpp"
|
|
#include "../op_inf_engine.hpp"
|
|
#include "../op_cann.hpp"
|
|
#include "../net_impl.hpp"
|
|
#include <opencv2/imgproc.hpp>
|
|
|
|
// Implements ONNX Resize operator semantics (ai.onnx) as per onnx.ai documentation.
|
|
// See: https://onnx.ai/onnx/operators/onnx__Resize.html (opsets 10, 11, 13, 18 supported)
|
|
|
|
#ifdef HAVE_DNN_NGRAPH
|
|
#include "../ie_ngraph.hpp"
|
|
#include <openvino/op/interpolate.hpp>
|
|
#endif
|
|
|
|
#ifdef HAVE_CUDA
|
|
#include "../cuda4dnn/primitives/resize.hpp"
|
|
using namespace cv::dnn::cuda4dnn;
|
|
#endif
|
|
|
|
namespace cv { namespace dnn {
|
|
|
|
namespace {
|
|
|
|
enum class CoordTransMode {
|
|
HALF_PIXEL,
|
|
PYTORCH_HALF_PIXEL,
|
|
TF_HALF_PIXEL_FOR_NN,
|
|
TF_CROP_AND_RESIZE,
|
|
HALF_PIXEL_SYMMETRIC,
|
|
ASYMMETRIC
|
|
};
|
|
|
|
static inline CoordTransMode parseCoordTransMode(const String& s)
|
|
{
|
|
if (s == "half_pixel") return CoordTransMode::HALF_PIXEL;
|
|
if (s == "pytorch_half_pixel") return CoordTransMode::PYTORCH_HALF_PIXEL;
|
|
if (s == "tf_half_pixel_for_nn") return CoordTransMode::TF_HALF_PIXEL_FOR_NN;
|
|
if (s == "tf_crop_and_resize") return CoordTransMode::TF_CROP_AND_RESIZE;
|
|
if (s == "half_pixel_symmetric") return CoordTransMode::HALF_PIXEL_SYMMETRIC;
|
|
return CoordTransMode::ASYMMETRIC;
|
|
}
|
|
|
|
enum class NearestMode {
|
|
FLOOR,
|
|
CEIL,
|
|
ROUND_PREFER_CEIL,
|
|
ROUND_PREFER_FLOOR
|
|
};
|
|
|
|
using std::clamp;
|
|
|
|
static inline NearestMode parseNearestMode(const String& s)
|
|
{
|
|
if (s == "floor") return NearestMode::FLOOR;
|
|
if (s == "ceil") return NearestMode::CEIL;
|
|
if (s == "round_prefer_ceil") return NearestMode::ROUND_PREFER_CEIL;
|
|
return NearestMode::ROUND_PREFER_FLOOR;
|
|
}
|
|
|
|
static constexpr int kResizeNumStripes = 16;
|
|
|
|
inline float computeSrcGeneric(int dst, float scale, int limit, int len,
|
|
CoordTransMode coordTransMode, bool /*halfPixelCenters*/,
|
|
float start_coord = 0.0f, float end_coord = 1.0f)
|
|
{
|
|
if (coordTransMode == CoordTransMode::TF_CROP_AND_RESIZE)
|
|
{
|
|
if (len > 1)
|
|
return start_coord * (limit - 1) + dst * (end_coord - start_coord) * (limit - 1) / float(len - 1);
|
|
else
|
|
return 0.5f * (start_coord + end_coord) * (limit - 1);
|
|
}
|
|
if (coordTransMode == CoordTransMode::PYTORCH_HALF_PIXEL)
|
|
return (len > 1) ? (dst + 0.5f)*scale - 0.5f : 0.f;
|
|
if (coordTransMode == CoordTransMode::HALF_PIXEL)
|
|
return (dst + 0.5f)*scale - 0.5f;
|
|
if (coordTransMode == CoordTransMode::TF_HALF_PIXEL_FOR_NN)
|
|
return (dst + 0.5f)*scale;
|
|
if (coordTransMode == CoordTransMode::HALF_PIXEL_SYMMETRIC)
|
|
{
|
|
// ONNX half_pixel_symmetric: offset = center*(1 - len_resized/(len_orig*x_scale)),
|
|
// with scale == 1/x_scale and limit == input length.
|
|
const float offset = limit*0.5f - len*scale*0.5f;
|
|
return offset + (dst + 0.5f)*scale - 0.5f;
|
|
}
|
|
return dst*scale;
|
|
}
|
|
|
|
static inline void buildNearestIndexMap(std::vector<int>& map,
|
|
int outLen,
|
|
int inLen,
|
|
float scale,
|
|
int len,
|
|
float start_coord,
|
|
float end_coord,
|
|
CoordTransMode coordTransMode,
|
|
NearestMode nearestMode,
|
|
bool halfPixelCenters)
|
|
{
|
|
auto nearestIndex = [&](float src) {
|
|
const int f = cvFloor(src);
|
|
const float frac = src - f;
|
|
const float eps = 1e-6f;
|
|
int idx;
|
|
if (nearestMode == NearestMode::FLOOR) idx = cvFloor(src);
|
|
else if (nearestMode == NearestMode::CEIL) idx = cvCeil(src);
|
|
else if (nearestMode == NearestMode::ROUND_PREFER_CEIL) {
|
|
idx = (abs(frac - 0.5f) <= eps) ? (f + 1) : cvRound(src);
|
|
} else {
|
|
idx = (abs(frac - 0.5f) <= eps) ? f : cvRound(src);
|
|
}
|
|
return clamp(idx, 0, inLen - 1);
|
|
};
|
|
|
|
map.resize(outLen);
|
|
for (int i = 0; i < outLen; ++i)
|
|
{
|
|
float src = computeSrcGeneric(i, scale, inLen, len,
|
|
coordTransMode, halfPixelCenters, start_coord, end_coord);
|
|
if (coordTransMode == CoordTransMode::TF_CROP_AND_RESIZE) {
|
|
if (src < 0.f || src >= float(inLen)) {
|
|
map[i] = -1; // out of bounds
|
|
continue;
|
|
}
|
|
} else {
|
|
src = std::min(std::max(src, 0.f), float(inLen - 1));
|
|
}
|
|
map[i] = nearestIndex(src);
|
|
}
|
|
}
|
|
|
|
static inline void buildBilinearIndexAndLerp(std::vector<int>& i0,
|
|
std::vector<int>& i1,
|
|
std::vector<float>& frac,
|
|
std::vector<uint8_t>& outOfBounds,
|
|
int outLen,
|
|
int inLen,
|
|
float scale,
|
|
int len,
|
|
float start_coord,
|
|
float end_coord,
|
|
CoordTransMode coordTransMode,
|
|
bool halfPixelCenters,
|
|
bool tf_crop_and_resize_mode)
|
|
{
|
|
i0.resize(outLen);
|
|
i1.resize(outLen);
|
|
frac.resize(outLen);
|
|
outOfBounds.assign(outLen, 0);
|
|
|
|
for (int o = 0; o < outLen; ++o)
|
|
{
|
|
float src = computeSrcGeneric(o, scale, inLen, len,
|
|
coordTransMode, halfPixelCenters, start_coord, end_coord);
|
|
if (tf_crop_and_resize_mode)
|
|
{
|
|
int base = int(std::floor(src));
|
|
if (base < 0 || base >= inLen - 1) {
|
|
outOfBounds[o] = 1;
|
|
i0[o] = 0;
|
|
i1[o] = 0;
|
|
frac[o] = 0.0f;
|
|
} else {
|
|
i0[o] = base;
|
|
i1[o] = base + 1;
|
|
frac[o] = src - float(base);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
src = std::min(std::max(src, 0.f), std::max(0.f, float(inLen - 1) - 1e-6f));
|
|
int base = int(std::floor(src));
|
|
i0[o] = clamp(base, 0, inLen - 1);
|
|
i1[o] = clamp(base + 1, 0, inLen - 1);
|
|
frac[o] = src - float(base);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void interpolateCubicResize(float x, float A, float* coeffs )
|
|
{
|
|
coeffs[0] = ((A*(x + 1) - 5*A)*(x + 1) + 8*A)*(x + 1) - 4*A;
|
|
coeffs[1] = ((A + 2)*x - (A + 3))*x*x + 1;
|
|
coeffs[2] = ((A + 2)*(1 - x) - (A + 3))*(1 - x)*(1 - x) + 1;
|
|
coeffs[3] = 1.f - coeffs[0] - coeffs[1] - coeffs[2];
|
|
}
|
|
|
|
static inline void buildCubicIndexAndWeights(std::vector<std::array<int,4>>& ids,
|
|
std::vector<std::array<float,4>>& weights,
|
|
std::vector<uint8_t>& outOfBounds,
|
|
int outLen,
|
|
int inLen,
|
|
float scale,
|
|
int len,
|
|
float start_coord,
|
|
float end_coord,
|
|
CoordTransMode coordTransMode,
|
|
bool halfPixelCenters,
|
|
bool tf_crop_and_resize_mode,
|
|
bool excludeOutside,
|
|
float cubicA)
|
|
{
|
|
ids.resize(outLen);
|
|
weights.resize(outLen);
|
|
outOfBounds.assign(outLen, 0);
|
|
|
|
for (int o = 0; o < outLen; ++o)
|
|
{
|
|
float src = computeSrcGeneric(o, scale, inLen, len,
|
|
coordTransMode, halfPixelCenters, start_coord, end_coord);
|
|
int i = int(std::floor(src));
|
|
float d = src - i;
|
|
float sw = 0.f;
|
|
bool hasOutOfBounds = false;
|
|
interpolateCubicResize(d, cubicA, weights[o].data());
|
|
|
|
if (!tf_crop_and_resize_mode && !excludeOutside)
|
|
{
|
|
for (int k = -1; k <= 2; ++k)
|
|
{
|
|
int idx = clamp(i + k, 0, inLen - 1);
|
|
ids[o][k+1] = idx;
|
|
sw += weights[o][k+1];
|
|
}
|
|
}
|
|
else if (tf_crop_and_resize_mode)
|
|
{
|
|
for (int k = -1; k <= 2; ++k)
|
|
{
|
|
int idx = i + k;
|
|
unsigned valid = (unsigned)idx < (unsigned)inLen;
|
|
ids[o][k+1] = valid ? idx : -1;
|
|
float w = weights[o][k+1];
|
|
float wv = valid ? w : 0.f;
|
|
weights[o][k+1] = wv;
|
|
sw += wv;
|
|
hasOutOfBounds |= !valid;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int k = -1; k <= 2; ++k)
|
|
{
|
|
int idx = i + k;
|
|
unsigned valid = (unsigned)idx < (unsigned)inLen;
|
|
ids[o][k+1] = valid ? idx : -1;
|
|
float w = weights[o][k+1];
|
|
float wv = valid ? w : 0.f;
|
|
weights[o][k+1] = wv;
|
|
sw += wv;
|
|
}
|
|
}
|
|
|
|
if (sw != 0.f)
|
|
for (int k = 0; k < 4; ++k)
|
|
weights[o][k] /= sw;
|
|
|
|
if (tf_crop_and_resize_mode && hasOutOfBounds)
|
|
outOfBounds[o] = 1;
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void resizeNearest(const Mat &inp, Mat &out,
|
|
float scaleH, float scaleW,
|
|
int lenY, int lenX,
|
|
NearestMode nearestMode,
|
|
const String &coordTransMode,
|
|
bool halfPixelCenters,
|
|
float start_y = 0.0f, float end_y = 1.0f,
|
|
float start_x = 0.0f, float end_x = 1.0f,
|
|
float extrapolation_value = 0.0f)
|
|
{
|
|
int inH = inp.size[2], inW = inp.size[3];
|
|
int outH = out.size[2], outW = out.size[3];
|
|
CV_Assert(inp.isContinuous() && out.isContinuous());
|
|
|
|
CoordTransMode coordMode = parseCoordTransMode(coordTransMode);
|
|
const bool tf_crop_and_resize_mode = (coordMode == CoordTransMode::TF_CROP_AND_RESIZE);
|
|
|
|
std::vector<int> mapY(outH);
|
|
buildNearestIndexMap(mapY, outH, inH, scaleH, lenY, start_y, end_y,
|
|
coordMode, nearestMode, halfPixelCenters);
|
|
|
|
std::vector<int> mapX(outW);
|
|
buildNearestIndexMap(mapX, outW, inW, scaleW, lenX, start_x, end_x,
|
|
coordMode, nearestMode, halfPixelCenters);
|
|
|
|
if (inp.shape().layout == DATA_LAYOUT_BLOCK) {
|
|
CV_Assert(inp.dims == 5 && out.dims == 5);
|
|
const int N = inp.size[0], C1 = inp.size[1], C0 = inp.size[4];
|
|
|
|
const size_t inStep0 = inp.step.p[0] / inp.elemSize();
|
|
const size_t inStep1 = inp.step.p[1] / inp.elemSize();
|
|
const size_t inStep2 = inp.step.p[2] / inp.elemSize();
|
|
const size_t inStep3 = inp.step.p[3] / inp.elemSize();
|
|
const size_t outStep0 = out.step.p[0] / out.elemSize();
|
|
const size_t outStep1 = out.step.p[1] / out.elemSize();
|
|
const size_t outStep2 = out.step.p[2] / out.elemSize();
|
|
const size_t outStep3 = out.step.p[3] / out.elemSize();
|
|
const size_t C0bytes = (size_t)C0 * sizeof(T);
|
|
|
|
const int nplanes = N * C1 * outH;
|
|
parallel_for_(Range(0, nplanes), [&](const Range& range) {
|
|
const T* inptr0 = reinterpret_cast<const T*>(inp.data);
|
|
T* outptr0 = reinterpret_cast<T*>(out.data);
|
|
T ext = saturate_cast<T>(extrapolation_value);
|
|
|
|
for (int plane = range.start; plane < range.end; ++plane) {
|
|
int t = plane;
|
|
int oy = t % outH; t /= outH;
|
|
int c1 = t % C1;
|
|
int n = t / C1;
|
|
|
|
int iy = mapY[oy];
|
|
T* outRow = outptr0 + n * outStep0 + c1 * outStep1 + oy * outStep2;
|
|
|
|
if (tf_crop_and_resize_mode && iy == -1) {
|
|
for (int ox = 0; ox < outW; ++ox) {
|
|
T* outPix = outRow + ox * outStep3;
|
|
for (int c0 = 0; c0 < C0; ++c0) outPix[c0] = ext;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const T* inRow = inptr0 + n * inStep0 + c1 * inStep1 + iy * inStep2;
|
|
for (int ox = 0; ox < outW; ++ox) {
|
|
int ix = mapX[ox];
|
|
if (tf_crop_and_resize_mode && ix == -1) {
|
|
T* outPix = outRow + ox * outStep3;
|
|
for (int c0 = 0; c0 < C0; ++c0) outPix[c0] = ext;
|
|
} else {
|
|
memcpy(outRow + ox * outStep3, inRow + ix * inStep3, C0bytes);
|
|
}
|
|
}
|
|
}
|
|
}, kResizeNumStripes);
|
|
return;
|
|
}
|
|
|
|
int numPlanes = inp.size[0] * inp.size[1];
|
|
Mat inpP = inp.reshape(1, numPlanes * inH);
|
|
Mat outP = out.reshape(1, numPlanes * outH);
|
|
|
|
const int nstripes = kResizeNumStripes;
|
|
parallel_for_(Range(0, nstripes), [&](const Range& range) {
|
|
int row0 = range.start * (outH * numPlanes) / nstripes;
|
|
float extrapolation_value_ = extrapolation_value;
|
|
int row1 = range.end * (outH * numPlanes) / nstripes - 1;
|
|
int plane0 = row0 / outH, plane1 = row1 / outH;
|
|
row0 %= outH;
|
|
row1 %= outH;
|
|
|
|
const int* mapYptr = mapY.data();
|
|
const int* mapXptr = mapX.data();
|
|
|
|
for (int p = plane0; p <= plane1; p++) {
|
|
int y0 = p == plane0 ? row0 : 0;
|
|
int y1 = p == plane1 ? row1 : outH - 1;
|
|
for (int y = y0; y <= y1; y++) {
|
|
int my = mapYptr[y];
|
|
if (tf_crop_and_resize_mode && my == -1) {
|
|
T* outRowFill = outP.ptr<T>(p * outH + y);
|
|
for (int x = 0; x < outW; ++x)
|
|
outRowFill[x] = T(extrapolation_value_);
|
|
continue;
|
|
}
|
|
const T* inpRow = inpP.ptr<T>(p * inH + my);
|
|
T* outRow = outP.ptr<T>(p * outH + y);
|
|
for (int x = 0; x < outW; ++x)
|
|
{
|
|
int mx = mapXptr[x];
|
|
if (tf_crop_and_resize_mode && mx == -1) {
|
|
outRow[x] = T(extrapolation_value_);
|
|
} else {
|
|
outRow[x] = inpRow[mx];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, nstripes);
|
|
}
|
|
|
|
template<typename T>
|
|
void resizeBilinear(const Mat &inp, Mat &out,
|
|
float scaleH, float scaleW,
|
|
int lenY, int lenX,
|
|
const String &coordTransMode,
|
|
bool halfPixelCenters,
|
|
float start_y = 0.0f, float end_y = 1.0f,
|
|
float start_x = 0.0f, float end_x = 1.0f,
|
|
float extrapolation_value = 0.0f)
|
|
{
|
|
int inH = inp.size[2], inW = inp.size[3];
|
|
int outH = out.size[2], outW = out.size[3];
|
|
CV_Assert(inp.isContinuous() && out.isContinuous());
|
|
|
|
CoordTransMode coordMode = parseCoordTransMode(coordTransMode);
|
|
const bool tf_crop_and_resize_mode = (coordMode == CoordTransMode::TF_CROP_AND_RESIZE);
|
|
|
|
std::vector<int> x0(outW), x1(outW);
|
|
std::vector<float> lx(outW);
|
|
std::vector<uint8_t> outOfBoundsX(outW);
|
|
buildBilinearIndexAndLerp(x0, x1, lx, outOfBoundsX,
|
|
outW, inW, scaleW, lenX, start_x, end_x,
|
|
coordMode, halfPixelCenters, tf_crop_and_resize_mode);
|
|
|
|
std::vector<int> y0(outH), y1(outH);
|
|
std::vector<float> ly(outH);
|
|
std::vector<uint8_t> outOfBoundsY(outH);
|
|
buildBilinearIndexAndLerp(y0, y1, ly, outOfBoundsY,
|
|
outH, inH, scaleH, lenY, start_y, end_y,
|
|
coordMode, halfPixelCenters, tf_crop_and_resize_mode);
|
|
|
|
if (inp.shape().layout == DATA_LAYOUT_BLOCK) {
|
|
CV_Assert(inp.dims == 5 && out.dims == 5);
|
|
const int N = inp.size[0], C1 = inp.size[1], C0 = inp.size[4];
|
|
|
|
const size_t inStep0 = inp.step.p[0] / inp.elemSize();
|
|
const size_t inStep1 = inp.step.p[1] / inp.elemSize();
|
|
const size_t inStep2 = inp.step.p[2] / inp.elemSize();
|
|
const size_t inStep3 = inp.step.p[3] / inp.elemSize();
|
|
const size_t outStep0 = out.step.p[0] / out.elemSize();
|
|
const size_t outStep1 = out.step.p[1] / out.elemSize();
|
|
const size_t outStep2 = out.step.p[2] / out.elemSize();
|
|
const size_t outStep3 = out.step.p[3] / out.elemSize();
|
|
|
|
const int nplanes = N * C1 * outH;
|
|
parallel_for_(Range(0, nplanes), [&](const Range& range) {
|
|
const T* inptr0 = reinterpret_cast<const T*>(inp.data);
|
|
T* outptr0 = reinterpret_cast<T*>(out.data);
|
|
T ext = saturate_cast<T>(extrapolation_value);
|
|
|
|
for (int plane = range.start; plane < range.end; ++plane) {
|
|
int t = plane;
|
|
int oy = t % outH; t /= outH;
|
|
int c1 = t % C1;
|
|
int n = t / C1;
|
|
|
|
T* outRow = outptr0 + n * outStep0 + c1 * outStep1 + oy * outStep2;
|
|
if (tf_crop_and_resize_mode && outOfBoundsY[oy]) {
|
|
for (int ox = 0; ox < outW; ++ox) {
|
|
T* outPix = outRow + ox * outStep3;
|
|
for (int c0 = 0; c0 < C0; ++c0) outPix[c0] = ext;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const T* row00 = inptr0 + n * inStep0 + c1 * inStep1 + y0[oy] * inStep2;
|
|
const T* row01 = inptr0 + n * inStep0 + c1 * inStep1 + y1[oy] * inStep2;
|
|
float fy = ly[oy];
|
|
|
|
for (int ox = 0; ox < outW; ++ox) {
|
|
T* outPix = outRow + ox * outStep3;
|
|
if (tf_crop_and_resize_mode && outOfBoundsX[ox]) {
|
|
for (int c0 = 0; c0 < C0; ++c0) outPix[c0] = ext;
|
|
continue;
|
|
}
|
|
const T* p00 = row00 + x0[ox] * inStep3;
|
|
const T* p01 = row00 + x1[ox] * inStep3;
|
|
const T* p10 = row01 + x0[ox] * inStep3;
|
|
const T* p11 = row01 + x1[ox] * inStep3;
|
|
float fx = lx[ox];
|
|
for (int c0 = 0; c0 < C0; ++c0) {
|
|
float top = static_cast<float>(p00[c0]) + fx * (static_cast<float>(p01[c0]) - static_cast<float>(p00[c0]));
|
|
float bot = static_cast<float>(p10[c0]) + fx * (static_cast<float>(p11[c0]) - static_cast<float>(p10[c0]));
|
|
outPix[c0] = saturate_cast<T>(top + fy * (bot - top));
|
|
}
|
|
}
|
|
}
|
|
}, kResizeNumStripes);
|
|
return;
|
|
}
|
|
|
|
int numPlanes = inp.size[0] * inp.size[1];
|
|
Mat inpP = inp.reshape(1, numPlanes * inH);
|
|
Mat outP = out.reshape(1, numPlanes * outH);
|
|
|
|
const int nstripes = kResizeNumStripes;
|
|
parallel_for_(Range(0, nstripes), [&](const Range& range) {
|
|
int row0 = range.start * (outH * numPlanes) / nstripes;
|
|
int row1 = range.end * (outH * numPlanes) / nstripes - 1;
|
|
int plane0 = row0 / outH, plane1 = row1 / outH;
|
|
row0 %= outH;
|
|
row1 %= outH;
|
|
|
|
const int* y0ptr = y0.data();
|
|
const int* y1ptr = y1.data();
|
|
const float* lyptr = ly.data();
|
|
const int* x0ptr = x0.data();
|
|
const float* lxptr = lx.data();
|
|
const uint8_t* outOfBoundsYptr = outOfBoundsY.data();
|
|
const uint8_t* outOfBoundsXptr = outOfBoundsX.data();
|
|
float extrapolation_value_ = extrapolation_value;
|
|
const bool tf_crop_and_resize_mode_ = tf_crop_and_resize_mode;
|
|
std::vector<float> hbufbuf(inW + 3);
|
|
float* hbuf = hbufbuf.data() + 1;
|
|
|
|
for (int p = plane0; p <= plane1; ++p)
|
|
{
|
|
int oy0 = (p == plane0) ? row0 : 0;
|
|
int oy1 = (p == plane1) ? row1 : outH - 1;
|
|
for (int oy = oy0; oy <= oy1; ++oy)
|
|
{
|
|
if (tf_crop_and_resize_mode_ && outOfBoundsYptr[oy]) {
|
|
T* outRowFill = outP.ptr<T>(p * outH + oy);
|
|
for (int ox = 0; ox < outW; ++ox)
|
|
outRowFill[ox] = T(extrapolation_value_);
|
|
continue;
|
|
}
|
|
|
|
const T* row0ptr = inpP.ptr<T>( p * inH + y0ptr[oy] );
|
|
const T* row1ptr = inpP.ptr<T>( p * inH + y1ptr[oy] );
|
|
float fy = lyptr[oy];
|
|
|
|
T* outRowBase = outP.ptr<T>( p * outH + oy );
|
|
|
|
for (int ix = 0; ix < inW; ++ix)
|
|
{
|
|
float v0 = static_cast<float>(row0ptr[ix]);
|
|
float v1 = static_cast<float>(row1ptr[ix]);
|
|
hbuf[ix] = v0 + fy * (v1 - v0);
|
|
}
|
|
hbuf[-1] = hbuf[0];
|
|
hbuf[inW] = hbuf[inW - 1];
|
|
hbuf[inW + 1] = hbuf[inW - 1];
|
|
|
|
for (int ox = 0; ox < outW; ++ox)
|
|
{
|
|
if (tf_crop_and_resize_mode_ && outOfBoundsXptr[ox]) {
|
|
outRowBase[ox] = T(extrapolation_value_);
|
|
} else {
|
|
int xi = x0ptr[ox];
|
|
float fx = lxptr[ox];
|
|
|
|
float left = hbuf[xi];
|
|
float res = left + fx * (hbuf[xi + 1] - left);
|
|
outRowBase[ox] = T(res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, nstripes);
|
|
}
|
|
|
|
template<typename T>
|
|
void resizeCubic(const Mat &inp, Mat &out,
|
|
float scaleH, float scaleW,
|
|
int lenY, int lenX,
|
|
float cubicA, bool excludeOutside,
|
|
const String &coordTransMode, bool halfPixelCenters,
|
|
float start_y = 0.0f, float end_y = 1.0f,
|
|
float start_x = 0.0f, float end_x = 1.0f,
|
|
float extrapolation_value = 0.0f)
|
|
{
|
|
int inH = inp.size[2], inW = inp.size[3];
|
|
int outH = out.size[2], outW = out.size[3];
|
|
|
|
CoordTransMode coordMode = parseCoordTransMode(coordTransMode);
|
|
const bool tf_crop_and_resize_mode = (coordMode == CoordTransMode::TF_CROP_AND_RESIZE);
|
|
|
|
std::vector<std::array<int,4>> x_id(outW);
|
|
std::vector<std::array<float,4>> x_w (outW);
|
|
std::vector<uint8_t> outOfBoundsX(outW);
|
|
buildCubicIndexAndWeights(x_id, x_w, outOfBoundsX,
|
|
outW, inW, scaleW, lenX, start_x, end_x,
|
|
coordMode, halfPixelCenters, tf_crop_and_resize_mode,
|
|
excludeOutside, cubicA);
|
|
|
|
std::vector<std::array<int,4>> y_id(outH);
|
|
std::vector<std::array<float,4>> y_w (outH);
|
|
std::vector<uint8_t> outOfBoundsY(outH);
|
|
buildCubicIndexAndWeights(y_id, y_w, outOfBoundsY,
|
|
outH, inH, scaleH, lenY, start_y, end_y,
|
|
coordMode, halfPixelCenters, tf_crop_and_resize_mode,
|
|
excludeOutside, cubicA);
|
|
|
|
if (inp.shape().layout == DATA_LAYOUT_BLOCK) {
|
|
CV_Assert(inp.dims == 5 && out.dims == 5);
|
|
CV_Assert(inp.isContinuous() && out.isContinuous());
|
|
const int N = inp.size[0], C1 = inp.size[1], C0 = inp.size[4];
|
|
|
|
const size_t inStep0 = inp.step.p[0] / inp.elemSize();
|
|
const size_t inStep1 = inp.step.p[1] / inp.elemSize();
|
|
const size_t inStep2 = inp.step.p[2] / inp.elemSize();
|
|
const size_t inStep3 = inp.step.p[3] / inp.elemSize();
|
|
const size_t outStep0 = out.step.p[0] / out.elemSize();
|
|
const size_t outStep1 = out.step.p[1] / out.elemSize();
|
|
const size_t outStep2 = out.step.p[2] / out.elemSize();
|
|
const size_t outStep3 = out.step.p[3] / out.elemSize();
|
|
|
|
const int nplanes = N * C1 * outH;
|
|
parallel_for_(Range(0, nplanes), [&](const Range& range) {
|
|
const T* inptr0 = reinterpret_cast<const T*>(inp.data);
|
|
T* outptr0 = reinterpret_cast<T*>(out.data);
|
|
T ext = saturate_cast<T>(extrapolation_value);
|
|
|
|
for (int plane = range.start; plane < range.end; ++plane) {
|
|
int t = plane;
|
|
int oy = t % outH; t /= outH;
|
|
int c1 = t % C1;
|
|
int n = t / C1;
|
|
|
|
T* outRow = outptr0 + n * outStep0 + c1 * outStep1 + oy * outStep2;
|
|
if (tf_crop_and_resize_mode && outOfBoundsY[oy]) {
|
|
for (int ox = 0; ox < outW; ++ox) {
|
|
T* outPix = outRow + ox * outStep3;
|
|
for (int c0 = 0; c0 < C0; ++c0) outPix[c0] = ext;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const int yy0 = y_id[oy][0], yy1 = y_id[oy][1], yy2 = y_id[oy][2], yy3 = y_id[oy][3];
|
|
const float wy0 = y_w[oy][0], wy1 = y_w[oy][1], wy2 = y_w[oy][2], wy3 = y_w[oy][3];
|
|
|
|
const T* row0 = yy0 >= 0 ? inptr0 + n * inStep0 + c1 * inStep1 + yy0 * inStep2 : nullptr;
|
|
const T* row1 = yy1 >= 0 ? inptr0 + n * inStep0 + c1 * inStep1 + yy1 * inStep2 : nullptr;
|
|
const T* row2 = yy2 >= 0 ? inptr0 + n * inStep0 + c1 * inStep1 + yy2 * inStep2 : nullptr;
|
|
const T* row3 = yy3 >= 0 ? inptr0 + n * inStep0 + c1 * inStep1 + yy3 * inStep2 : nullptr;
|
|
|
|
for (int ox = 0; ox < outW; ++ox) {
|
|
T* outPix = outRow + ox * outStep3;
|
|
if (tf_crop_and_resize_mode && outOfBoundsX[ox]) {
|
|
for (int c0 = 0; c0 < C0; ++c0) outPix[c0] = ext;
|
|
continue;
|
|
}
|
|
const int xx0 = x_id[ox][0], xx1 = x_id[ox][1], xx2 = x_id[ox][2], xx3 = x_id[ox][3];
|
|
const float wx0 = x_w[ox][0], wx1 = x_w[ox][1], wx2 = x_w[ox][2], wx3 = x_w[ox][3];
|
|
|
|
for (int c0 = 0; c0 < C0; ++c0) {
|
|
float val = 0.f;
|
|
if (row0) {
|
|
if (xx0 >= 0) val += wy0 * wx0 * static_cast<float>(row0[xx0 * inStep3 + c0]);
|
|
if (xx1 >= 0) val += wy0 * wx1 * static_cast<float>(row0[xx1 * inStep3 + c0]);
|
|
if (xx2 >= 0) val += wy0 * wx2 * static_cast<float>(row0[xx2 * inStep3 + c0]);
|
|
if (xx3 >= 0) val += wy0 * wx3 * static_cast<float>(row0[xx3 * inStep3 + c0]);
|
|
}
|
|
if (row1) {
|
|
if (xx0 >= 0) val += wy1 * wx0 * static_cast<float>(row1[xx0 * inStep3 + c0]);
|
|
if (xx1 >= 0) val += wy1 * wx1 * static_cast<float>(row1[xx1 * inStep3 + c0]);
|
|
if (xx2 >= 0) val += wy1 * wx2 * static_cast<float>(row1[xx2 * inStep3 + c0]);
|
|
if (xx3 >= 0) val += wy1 * wx3 * static_cast<float>(row1[xx3 * inStep3 + c0]);
|
|
}
|
|
if (row2) {
|
|
if (xx0 >= 0) val += wy2 * wx0 * static_cast<float>(row2[xx0 * inStep3 + c0]);
|
|
if (xx1 >= 0) val += wy2 * wx1 * static_cast<float>(row2[xx1 * inStep3 + c0]);
|
|
if (xx2 >= 0) val += wy2 * wx2 * static_cast<float>(row2[xx2 * inStep3 + c0]);
|
|
if (xx3 >= 0) val += wy2 * wx3 * static_cast<float>(row2[xx3 * inStep3 + c0]);
|
|
}
|
|
if (row3) {
|
|
if (xx0 >= 0) val += wy3 * wx0 * static_cast<float>(row3[xx0 * inStep3 + c0]);
|
|
if (xx1 >= 0) val += wy3 * wx1 * static_cast<float>(row3[xx1 * inStep3 + c0]);
|
|
if (xx2 >= 0) val += wy3 * wx2 * static_cast<float>(row3[xx2 * inStep3 + c0]);
|
|
if (xx3 >= 0) val += wy3 * wx3 * static_cast<float>(row3[xx3 * inStep3 + c0]);
|
|
}
|
|
outPix[c0] = saturate_cast<T>(val);
|
|
}
|
|
}
|
|
}
|
|
}, kResizeNumStripes);
|
|
return;
|
|
}
|
|
|
|
int numPlanes = inp.size[0] * inp.size[1];
|
|
Mat inpPlanes = inp.reshape(1, numPlanes * inH);
|
|
Mat outPlanes = out.reshape(1, numPlanes * outH);
|
|
|
|
const int nstripes = kResizeNumStripes;
|
|
parallel_for_(Range(0, nstripes), [&](const Range& range) {
|
|
int row0 = range.start * (outH * numPlanes) / nstripes;
|
|
int row1 = range.end * (outH * numPlanes) / nstripes - 1;
|
|
int plane0 = row0 / outH, plane1 = row1 / outH;
|
|
row0 %= outH;
|
|
row1 %= outH;
|
|
|
|
const bool tf_crop_and_resize_mode_ = tf_crop_and_resize_mode;
|
|
const uint8_t* outOfBoundsYptr = outOfBoundsY.data();
|
|
const uint8_t* outOfBoundsXptr = outOfBoundsX.data();
|
|
float extrapolation_value_ = extrapolation_value;
|
|
std::vector<float> hbuf(inW, 0.f);
|
|
|
|
for (int p = plane0; p <= plane1; ++p)
|
|
{
|
|
int oy0 = (p == plane0) ? row0 : 0;
|
|
int oy1 = (p == plane1) ? row1 : outH - 1;
|
|
const T* inpBase = inpPlanes.ptr<T>(p * inH);
|
|
for (int oy = oy0; oy <= oy1; ++oy)
|
|
{
|
|
T* outRow = outPlanes.ptr<T>(p * outH) + oy * outW;
|
|
|
|
if (tf_crop_and_resize_mode_ && outOfBoundsYptr[oy]) {
|
|
for (int ox = 0; ox < outW; ++ox)
|
|
outRow[ox] = cv::saturate_cast<T>(extrapolation_value_);
|
|
continue;
|
|
}
|
|
|
|
const float w0y = y_w[oy][0];
|
|
const float w1y = y_w[oy][1];
|
|
const float w2y = y_w[oy][2];
|
|
const float w3y = y_w[oy][3];
|
|
|
|
int yy0, yy1, yy2, yy3;
|
|
yy0 = y_id[oy][0];
|
|
yy1 = y_id[oy][1];
|
|
yy2 = y_id[oy][2];
|
|
yy3 = y_id[oy][3];
|
|
|
|
const T* ptr0 = (yy0 >= 0) ? (inpBase + (size_t)yy0 * inW) : nullptr;
|
|
const T* ptr1 = (yy1 >= 0) ? (inpBase + (size_t)yy1 * inW) : nullptr;
|
|
const T* ptr2 = (yy2 >= 0) ? (inpBase + (size_t)yy2 * inW) : nullptr;
|
|
const T* ptr3 = (yy3 >= 0) ? (inpBase + (size_t)yy3 * inW) : nullptr;
|
|
|
|
if (!ptr0 && !ptr1 && !ptr2 && !ptr3) {
|
|
for (int ix = 0; ix < inW; ++ix)
|
|
{
|
|
hbuf[ix] = 0.f;
|
|
}
|
|
} else {
|
|
const T* ptrNZ = ptr0 ? ptr0 : (ptr1 ? ptr1 : (ptr2 ? ptr2 : ptr3));
|
|
float w0 = ptr0 ? w0y : 0.f;
|
|
float w1 = ptr1 ? w1y : 0.f;
|
|
float w2 = ptr2 ? w2y : 0.f;
|
|
float w3 = ptr3 ? w3y : 0.f;
|
|
if (!ptr0) ptr0 = ptrNZ;
|
|
if (!ptr1) ptr1 = ptrNZ;
|
|
if (!ptr2) ptr2 = ptrNZ;
|
|
if (!ptr3) ptr3 = ptrNZ;
|
|
|
|
for (int ix = 0; ix < inW; ++ix)
|
|
{
|
|
hbuf[ix] = static_cast<float>(ptr0[ix]) * w0 +
|
|
static_cast<float>(ptr1[ix]) * w1 +
|
|
static_cast<float>(ptr2[ix]) * w2 +
|
|
static_cast<float>(ptr3[ix]) * w3;
|
|
}
|
|
}
|
|
|
|
for (int ox = 0; ox < outW; ++ox)
|
|
{
|
|
if (tf_crop_and_resize_mode_ && outOfBoundsXptr[ox]) {
|
|
outRow[ox] = cv::saturate_cast<T>(extrapolation_value_);
|
|
continue;
|
|
}
|
|
const int xx = x_id[ox][1];
|
|
const float w0x = x_w[ox][0];
|
|
const float w1x = x_w[ox][1];
|
|
const float w2x = x_w[ox][2];
|
|
const float w3x = x_w[ox][3];
|
|
float val;
|
|
if (1 <= xx && xx + 3 < inW) {
|
|
val = hbuf[xx - 1] * w0x + hbuf[xx] * w1x + hbuf[xx + 1] * w2x + hbuf[xx + 2] * w3x;
|
|
} else {
|
|
const int xx0 = x_id[ox][0];
|
|
const int xx1 = x_id[ox][1];
|
|
const int xx2 = x_id[ox][2];
|
|
const int xx3 = x_id[ox][3];
|
|
val = 0.f;
|
|
if (xx0 >= 0) val += hbuf[xx0] * w0x;
|
|
if (xx1 >= 0) val += hbuf[xx1] * w1x;
|
|
if (xx2 >= 0) val += hbuf[xx2] * w2x;
|
|
if (xx3 >= 0) val += hbuf[xx3] * w3x;
|
|
}
|
|
outRow[ox] = cv::saturate_cast<T>(val);
|
|
}
|
|
}
|
|
}
|
|
}, nstripes);
|
|
}
|
|
|
|
// ---- ONNX antialias (PIL-style) resampling ----------------------------------
|
|
static inline float aaTriangle(float x)
|
|
{
|
|
x = std::abs(x);
|
|
return x < 1.f ? 1.f - x : 0.f;
|
|
}
|
|
|
|
static inline float aaCubic(float x, float a)
|
|
{
|
|
x = std::abs(x);
|
|
if (x < 1.f) return ((a + 2.f)*x - (a + 3.f))*x*x + 1.f;
|
|
if (x < 2.f) return a*(((x - 5.f)*x + 8.f)*x - 4.f);
|
|
return 0.f;
|
|
}
|
|
|
|
// Per-output filter taps for one axis. After clamping out-of-bound samples to
|
|
// the edge (exclude_outside == false), the contributing indices are contiguous,
|
|
// so each output stores a start index 'lo', a 'cnt' and an offset into 'w'.
|
|
struct AAWeights
|
|
{
|
|
std::vector<int> lo, cnt, ofs;
|
|
std::vector<float> w;
|
|
};
|
|
|
|
static void buildAAWeights(AAWeights& p, int inS, int outS, float xscale,
|
|
bool cubic, float cubicA, CoordTransMode coordMode)
|
|
{
|
|
const float scaleC = 1.f / xscale; // input/output direction
|
|
const float radius = cubic ? 2.f : 1.f;
|
|
const float support = scaleC >= 1.f ? radius*scaleC : radius;
|
|
const float inv = scaleC >= 1.f ? 1.f/scaleC : 1.f;
|
|
|
|
p.lo.resize(outS); p.cnt.resize(outS); p.ofs.resize(outS);
|
|
p.w.clear();
|
|
std::vector<float> tmp;
|
|
for (int y = 0; y < outS; y++)
|
|
{
|
|
const float center = computeSrcGeneric(y, scaleC, inS, outS, coordMode, true);
|
|
const int xmin = (int)std::floor(center - support + 0.5f);
|
|
const int xmax = (int)std::floor(center + support + 0.5f); // inclusive
|
|
const int lo = std::min(std::max(xmin, 0), inS - 1);
|
|
const int hi = std::min(std::max(xmax, 0), inS - 1);
|
|
const int cnt = hi - lo + 1;
|
|
tmp.assign(cnt, 0.f);
|
|
float tot = 0.f;
|
|
for (int x = xmin; x <= xmax; x++)
|
|
{
|
|
const float wt = cubic ? aaCubic((x - center)*inv, cubicA)
|
|
: aaTriangle((x - center)*inv);
|
|
const int idx = std::min(std::max(x, 0), inS - 1);
|
|
tmp[idx - lo] += wt;
|
|
tot += wt;
|
|
}
|
|
p.lo[y] = lo; p.cnt[y] = cnt; p.ofs[y] = (int)p.w.size();
|
|
for (int k = 0; k < cnt; k++)
|
|
p.w.push_back(tot != 0.f ? tmp[k] / tot : 0.f);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void resizeAntialias(const Mat& inp, Mat& out,
|
|
float xscaleH, float xscaleW,
|
|
bool cubic, float cubicA, CoordTransMode coordMode)
|
|
{
|
|
CV_Assert(inp.dims == 4 && out.dims == 4 && inp.isContinuous() && out.isContinuous());
|
|
const int N = inp.size[0], C = inp.size[1];
|
|
const int inH = inp.size[2], inW = inp.size[3];
|
|
const int outH = out.size[2], outW = out.size[3];
|
|
|
|
AAWeights px, py;
|
|
buildAAWeights(px, inW, outW, xscaleW, cubic, cubicA, coordMode);
|
|
buildAAWeights(py, inH, outH, xscaleH, cubic, cubicA, coordMode);
|
|
|
|
const int planes = N * C;
|
|
parallel_for_(Range(0, planes), [&](const Range& r) {
|
|
std::vector<float> buf((size_t)inH * outW);
|
|
for (int pl = r.start; pl < r.end; pl++)
|
|
{
|
|
const T* inPlane = inp.ptr<T>(0) + (size_t)pl * inH * inW;
|
|
T* outPlane = out.ptr<T>(0) + (size_t)pl * outH * outW;
|
|
// Horizontal pass: inp[inH x inW] -> buf[inH x outW].
|
|
for (int y = 0; y < inH; y++)
|
|
{
|
|
const T* inRow = inPlane + (size_t)y * inW;
|
|
float* bufRow = buf.data() + (size_t)y * outW;
|
|
for (int ox = 0; ox < outW; ox++)
|
|
{
|
|
const float* w = px.w.data() + px.ofs[ox];
|
|
const int lo = px.lo[ox], cnt = px.cnt[ox];
|
|
float acc = 0.f;
|
|
for (int k = 0; k < cnt; k++)
|
|
acc += w[k] * (float)inRow[lo + k];
|
|
bufRow[ox] = acc;
|
|
}
|
|
}
|
|
// Vertical pass: buf[inH x outW] -> out[outH x outW].
|
|
for (int oy = 0; oy < outH; oy++)
|
|
{
|
|
const float* w = py.w.data() + py.ofs[oy];
|
|
const int lo = py.lo[oy], cnt = py.cnt[oy];
|
|
T* outRow = outPlane + (size_t)oy * outW;
|
|
for (int ox = 0; ox < outW; ox++)
|
|
{
|
|
float acc = 0.f;
|
|
for (int k = 0; k < cnt; k++)
|
|
acc += w[k] * buf[(size_t)(lo + k) * outW + ox];
|
|
outRow[ox] = saturate_cast<T>(acc);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
class Resize2LayerImpl : public Resize2Layer
|
|
{
|
|
public:
|
|
int outWidth0, outHeight0;
|
|
Resize2LayerImpl(const LayerParams& params) : zoomFactorWidth(params.get<float>("zoom_factor_x", params.get<float>("zoom_factor", 0))),
|
|
zoomFactorHeight(params.get<float>("zoom_factor_y", params.get<float>("zoom_factor", 0))),
|
|
scaleWidth(0), scaleHeight(0), cubicCoeffA(params.get<float>("cubic_coeff_a", -0.75f)),
|
|
roi_start_y(0.0f), roi_end_y(1.0f), roi_start_x(0.0f), roi_end_x(1.0f),
|
|
extrapolation_value(params.get<float>("extrapolation_value", 0.0f))
|
|
{
|
|
setParamsFrom(params);
|
|
outWidth = outWidth0 = params.get<float>("width", 0);
|
|
outHeight = outHeight0 = params.get<float>("height", 0);
|
|
if (params.has("zoom_factor"))
|
|
{
|
|
CV_Assert(!params.has("zoom_factor_x") && !params.has("zoom_factor_y"));
|
|
}
|
|
else if (params.has("zoom_factor_x") || params.has("zoom_factor_y"))
|
|
{
|
|
CV_Assert(params.has("zoom_factor_x") && params.has("zoom_factor_y"));
|
|
}
|
|
interpolation = params.get<String>("interpolation");
|
|
// Keep nearest_mode if provided (ONNX attribute). Default is "round_prefer_floor" as per ONNX spec.
|
|
nearestModeE = parseNearestMode(params.get<String>("nearest_mode", "round_prefer_floor"));
|
|
CV_Check(interpolation, interpolation == "nearest" || interpolation == "opencv_linear" || interpolation == "bilinear" || interpolation == "cubic", "");
|
|
|
|
excludeOutside = params.get<bool>("exclude_outside", false);
|
|
dynamicROI = params.get<bool>("dynamic_roi", false);
|
|
|
|
alignCorners = params.get<bool>("align_corners", false);
|
|
halfPixelCenters = params.get<bool>("half_pixel_centers", false);
|
|
coordTransMode = params.get<String>("coordinate_transformation_mode", "half_pixel");
|
|
coordTransModeE = parseCoordTransMode(coordTransMode);
|
|
|
|
if (interpolation == "opencv_linear")
|
|
halfPixelCenters = true;
|
|
|
|
keepAspectPolicy = params.get<String>("keep_aspect_ratio_policy", "stretch");
|
|
antialias = params.get<int>("antialias", 0) != 0;
|
|
if (params.has("axes")) {
|
|
const DictValue& a = params.get("axes");
|
|
axesAttr.resize(a.size());
|
|
for (int i = 0; i < a.size(); i++)
|
|
axesAttr[i] = a.get<int>(i);
|
|
}
|
|
}
|
|
|
|
// Map the H (axis 2) and W (axis 3) entries within a 2- or 4-element
|
|
// sizes/scales vector, honoring the ONNX "axes" attribute order.
|
|
void spatialIndices(size_t nelems, int& hIdx, int& wIdx) const
|
|
{
|
|
if (nelems == 4) { hIdx = 2; wIdx = 3; }
|
|
else { hIdx = 0; wIdx = 1; }
|
|
|
|
if (axesAttr.size() == nelems) {
|
|
int foundH = -1, foundW = -1;
|
|
for (size_t k = 0; k < nelems; k++) {
|
|
int ax = axesAttr[k] < 0 ? axesAttr[k] + 4 : axesAttr[k];
|
|
if (ax == 2) foundH = (int)k;
|
|
else if (ax == 3) foundW = (int)k;
|
|
}
|
|
if (foundH >= 0 && foundW >= 0) { hIdx = foundH; wIdx = foundW; }
|
|
}
|
|
}
|
|
|
|
bool dynamicOutputShapes() const CV_OVERRIDE
|
|
{
|
|
if (dynamicROI) return true;
|
|
size_t ninputs = inputs.size();
|
|
if (ninputs <= 1 &&
|
|
((outWidth0 > 0 && outHeight0 > 0) ||
|
|
(zoomFactorWidth > 0 && zoomFactorHeight > 0)))
|
|
return false;
|
|
Net::Impl* netimpl_ = getNetImpl(this);
|
|
if (!netimpl_)
|
|
return true;
|
|
for (size_t i = 1; i < ninputs; i++) {
|
|
if (!netimpl_->isConstArg(inputs[i]))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int getLayouts(const std::vector<DataLayout>& actualInputs,
|
|
std::vector<DataLayout>& desiredInputs,
|
|
const int requiredOutputs,
|
|
std::vector<DataLayout>& outputs) const CV_OVERRIDE
|
|
{
|
|
CV_Assert(!actualInputs.empty());
|
|
desiredInputs = actualInputs;
|
|
outputs.assign(requiredOutputs, actualInputs[0]);
|
|
|
|
if (actualInputs[0] != DATA_LAYOUT_BLOCK)
|
|
return 0;
|
|
|
|
if (interpolation == "nearest" || interpolation == "bilinear" || interpolation == "opencv_linear" || interpolation == "cubic") {
|
|
desiredInputs[0] = DATA_LAYOUT_BLOCK;
|
|
outputs.assign(requiredOutputs, DATA_LAYOUT_BLOCK);
|
|
} else {
|
|
Net::Impl* netimpl_ = getNetImpl(this);
|
|
DataLayout defaultLayout = netimpl_ ? netimpl_->originalLayout : DATA_LAYOUT_NCHW;
|
|
desiredInputs[0] = defaultLayout;
|
|
outputs.assign(requiredOutputs, DATA_LAYOUT_UNKNOWN);
|
|
}
|
|
return outputs[0] == DATA_LAYOUT_BLOCK ? getNetImpl(this)->defaultC0 : 0;
|
|
}
|
|
|
|
MatShape getOutShape(const MatShape& inpShape, const std::vector<int>& sizes,
|
|
const std::vector<float>& scales) const
|
|
{
|
|
// ONNX Resize allows either "sizes" or "scales" input. These tensors may
|
|
// describe all 4 dims (N,C,H,W) or only spatial dims (H,W) when accompanied
|
|
// by an "axes" input equal to {2,3}. To stay backwards-compatible, we keep
|
|
// the legacy 4-element handling but also accept 2-element vectors.
|
|
|
|
CV_Assert((sizes.empty() ^ scales.empty()) &&
|
|
(sizes.empty() ? (scales.size() == 4 || scales.size() == 2)
|
|
: (sizes.size() == 4 || sizes.size() == 2)));
|
|
|
|
MatShape outShape = inpShape;
|
|
const int inH = inpShape[2], inW = inpShape[3];
|
|
if (!sizes.empty()) {
|
|
int hIdx, wIdx;
|
|
spatialIndices(sizes.size(), hIdx, wIdx);
|
|
int szH = sizes[hIdx], szW = sizes[wIdx];
|
|
if (keepAspectPolicy == "not_larger" || keepAspectPolicy == "not_smaller") {
|
|
float scH = float(szH) / inH, scW = float(szW) / inW;
|
|
float sc = keepAspectPolicy == "not_larger" ? std::min(scH, scW)
|
|
: std::max(scH, scW);
|
|
outShape[2] = int(std::round(sc * inH));
|
|
outShape[3] = int(std::round(sc * inW));
|
|
} else {
|
|
outShape[2] = szH;
|
|
outShape[3] = szW;
|
|
}
|
|
} else {
|
|
int hIdx, wIdx;
|
|
spatialIndices(scales.size(), hIdx, wIdx);
|
|
outShape[2] = cvFloor(inH * scales[hIdx]);
|
|
outShape[3] = cvFloor(inW * scales[wIdx]);
|
|
}
|
|
return outShape;
|
|
}
|
|
|
|
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
|
const int requiredOutputs,
|
|
std::vector<MatShape> &outputs,
|
|
std::vector<MatShape> &internals) const CV_OVERRIDE
|
|
{
|
|
size_t ninputs = inputs.size();
|
|
CV_Assert(ninputs == 1 || ninputs == 2 || ninputs >= 4);
|
|
outputs.resize(1, inputs[0]);
|
|
|
|
// Rank-3 (N,C,W): 1-D resize of the W axis; width baked into params, stays rank-3.
|
|
if (inputs[0].dims == 3) {
|
|
CV_CheckEQ(ninputs, (size_t)1, "1-D Resize2 expects sizes baked as width param");
|
|
outputs[0][2] = zoomFactorWidth > 0 ? cvFloor(inputs[0][2] * zoomFactorWidth) : outWidth0;
|
|
return outputs[0][2] == inputs[0][2];
|
|
}
|
|
|
|
// New ONNX importer may provide "sizes" or "scales" via constant blobs
|
|
// (blobs[0] = roi, blobs[1] = scales, blobs[2] = sizes, blobs[3] = axes).
|
|
if (ninputs == 1 && !this->blobs.empty()) {
|
|
std::vector<int> sizes;
|
|
std::vector<float> scales;
|
|
if (this->blobs.size() >= 3 && this->blobs[2].total() > 0)
|
|
tensorToIntVec(this->blobs[2], sizes);
|
|
if (this->blobs.size() >= 2 && this->blobs[1].total() > 0)
|
|
tensorToFloatVec(this->blobs[1], scales);
|
|
|
|
if (!sizes.empty() || !scales.empty()) {
|
|
outputs[0] = getOutShape(inputs[0], sizes, scales);
|
|
// in-place if spatial dims unchanged
|
|
return (outputs[0][2] == inputs[0][2]) && (outputs[0][3] == inputs[0][3]);
|
|
}
|
|
}
|
|
|
|
if (ninputs == 1) {
|
|
outputs[0][2] = zoomFactorHeight > 0 ? cvFloor(inputs[0][2] * zoomFactorHeight) : outHeight0;
|
|
outputs[0][3] = zoomFactorWidth > 0 ? cvFloor(inputs[0][3] * zoomFactorWidth) : outWidth0;
|
|
} else if (ninputs == 2 && inputs[1].dims == 4) {
|
|
outputs[0][2] = inputs[1][2];
|
|
outputs[0][3] = inputs[1][3];
|
|
} else {
|
|
Net::Impl* netimpl_ = getNetImpl(this);
|
|
std::vector<int> sizes;
|
|
std::vector<float> scales;
|
|
if (ninputs >= 4) {
|
|
Mat sizesTensor = netimpl_->argTensor(this->inputs[3]);
|
|
tensorToIntVec(sizesTensor, sizes);
|
|
}
|
|
|
|
Mat scalesTensor = netimpl_->argTensor(this->inputs[(ninputs == 2) ? 1 : 2]);
|
|
tensorToFloatVec(scalesTensor, scales);
|
|
outputs[0] = getOutShape(inputs[0], sizes, scales);
|
|
}
|
|
// We can work in-place (do nothing) if input shape == output shape.
|
|
return (outputs[0][2] == inputs[0][2]) && (outputs[0][3] == inputs[0][3]);
|
|
}
|
|
|
|
// Only resizeNearest has a genuine CV_32S path; other modes reject it below.
|
|
void getTypes(const std::vector<MatType>& inputs,
|
|
const int requiredOutputs,
|
|
const int requiredInternals,
|
|
std::vector<MatType>& outputs,
|
|
std::vector<MatType>& internals) const CV_OVERRIDE
|
|
{
|
|
CV_Assert(inputs.size());
|
|
for (auto input : inputs)
|
|
CV_CheckType(input, input == CV_32F || input == CV_64F || input == CV_8S || input == CV_8U ||
|
|
input == CV_64S || input == CV_32S, "");
|
|
|
|
outputs.assign(requiredOutputs, inputs[0]);
|
|
internals.assign(requiredInternals, inputs[0]);
|
|
}
|
|
|
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
|
{
|
|
if (backendId == DNN_BACKEND_CUDA)
|
|
return interpolation == "nearest" || interpolation == "bilinear" || interpolation == "opencv_linear";
|
|
|
|
if (backendId == DNN_BACKEND_CANN)
|
|
return interpolation == "nearest" || interpolation == "bilinear" || interpolation == "opencv_linear";
|
|
|
|
#ifdef HAVE_INF_ENGINE
|
|
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
|
{
|
|
return (interpolation == "nearest" && scaleWidth == scaleHeight) ||
|
|
(interpolation == "bilinear");
|
|
}
|
|
#endif
|
|
return backendId == DNN_BACKEND_OPENCV;
|
|
}
|
|
|
|
void updateOutSizeAndScale(const MatShape& inpShape, const MatShape& outShape)
|
|
{
|
|
CV_Assert(inpShape.dims >= 4 && outShape.dims >= 4);
|
|
outHeight = outShape[2];
|
|
outWidth = outShape[3];
|
|
if (alignCorners && outHeight > 1)
|
|
scaleHeight = float(inpShape[2] - 1) / (outHeight - 1);
|
|
else
|
|
scaleHeight = float(inpShape[2]) / outHeight;
|
|
|
|
if (alignCorners && outWidth > 1)
|
|
scaleWidth = float(inpShape[3] - 1) / (outWidth - 1);
|
|
else
|
|
scaleWidth = float(inpShape[3]) / outWidth;
|
|
}
|
|
|
|
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr,
|
|
OutputArrayOfArrays internals_arr) CV_OVERRIDE
|
|
{
|
|
CV_TRACE_FUNCTION();
|
|
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
|
|
|
|
std::vector<int> sizes;
|
|
std::vector<float> scales;
|
|
std::vector<Mat> inputs;
|
|
inputs_arr.getMatVector(inputs);
|
|
size_t ninputs = inputs.size();
|
|
CV_Assert(ninputs > 0);
|
|
|
|
Mat& inp_ = inputs[0];
|
|
|
|
MatShape inpShape = inp_.shape();
|
|
MatShape outShape;
|
|
|
|
// Rank-3 (N,C,W): fold a unit H axis so the rank-4 kernels run, unfold on output.
|
|
const bool fold1d = (inp_.dims == 3);
|
|
MatShape outShape1d;
|
|
if (fold1d) {
|
|
int outW = zoomFactorWidth > 0 ? cvFloor(inpShape[2] * zoomFactorWidth) : outWidth0;
|
|
outShape1d = inpShape;
|
|
outShape1d[2] = outW;
|
|
inp_ = inp_.reshape(1, MatShape({inpShape[0], inpShape[1], 1, inpShape[2]}));
|
|
inpShape = inp_.shape();
|
|
}
|
|
|
|
if (ninputs == 1) {
|
|
outShape = inpShape;
|
|
outShape[2] = zoomFactorHeight > 0 ? cvFloor(inpShape[2] * zoomFactorHeight) : outHeight0;
|
|
outShape[3] = zoomFactorWidth > 0 ? cvFloor(inpShape[3] * zoomFactorWidth) : outWidth0;
|
|
} else if (ninputs == 2 && inputs[0].dims == 4 && inputs[1].dims == 4) {
|
|
outShape = inpShape;
|
|
outShape[2] = inputs[1].size[2];
|
|
outShape[3] = inputs[1].size[3];
|
|
} else {
|
|
if (ninputs >= 4) {
|
|
Mat sizesTensor = inputs[3];
|
|
tensorToIntVec(sizesTensor, sizes);
|
|
}
|
|
Mat scalesTensor = inputs[(ninputs == 2) ? 1 : 2];
|
|
tensorToFloatVec(scalesTensor, scales);
|
|
outShape = getOutShape(inpShape, sizes, scales);
|
|
}
|
|
|
|
int length_resized_y = outShape[2];
|
|
int length_resized_x = outShape[3];
|
|
updateOutSizeAndScale(inpShape, outShape);
|
|
|
|
// Read ROI if dynamicROI is enabled
|
|
if (dynamicROI && coordTransModeE == CoordTransMode::TF_CROP_AND_RESIZE && ninputs >= 2)
|
|
{
|
|
Mat roiTensor = inputs[1];
|
|
std::vector<float> roi;
|
|
tensorToFloatVec(roiTensor, roi);
|
|
if (axesAttr.size() == 2 && roi.size() == 4)
|
|
{
|
|
// ROI given per "axes": [start_axes[0], start_axes[1], end_axes[0], end_axes[1]]
|
|
float start[4] = {0.f, 0.f, 0.f, 0.f};
|
|
float end[4] = {1.f, 1.f, 1.f, 1.f};
|
|
for (int k = 0; k < 2; k++) {
|
|
int ax = axesAttr[k] < 0 ? axesAttr[k] + 4 : axesAttr[k];
|
|
start[ax] = roi[k];
|
|
end[ax] = roi[2 + k];
|
|
}
|
|
roi_start_y = start[2]; roi_start_x = start[3];
|
|
roi_end_y = end[2]; roi_end_x = end[3];
|
|
}
|
|
else if (roi.size() >= 4)
|
|
{
|
|
if (roi.size() == 4) {
|
|
roi_start_y = roi[0];
|
|
roi_start_x = roi[1];
|
|
roi_end_y = roi[2];
|
|
roi_end_x = roi[3];
|
|
} else if (roi.size() == 6) {
|
|
roi_start_y = roi[1];
|
|
roi_start_x = roi[2];
|
|
roi_end_y = roi[4];
|
|
roi_end_x = roi[5];
|
|
} else if (roi.size() == 8) {
|
|
roi_start_y = roi[2];
|
|
roi_start_x = roi[3];
|
|
roi_end_y = roi[6];
|
|
roi_end_x = roi[7];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sizes.empty() && !scales.empty() && halfPixelCenters)
|
|
{
|
|
int hIdx, wIdx;
|
|
spatialIndices(scales.size(), hIdx, wIdx);
|
|
scaleHeight = 1.f / scales[hIdx];
|
|
scaleWidth = 1.f / scales[wIdx];
|
|
}
|
|
else if (sizes.empty() && !scales.empty() && alignCorners)
|
|
{
|
|
int hIdx, wIdx;
|
|
spatialIndices(scales.size(), hIdx, wIdx);
|
|
float lenH = inpShape[2] * scales[hIdx];
|
|
float lenW = inpShape[3] * scales[wIdx];
|
|
if (lenH > 1.f) scaleHeight = float(inpShape[2] - 1) / (lenH - 1.f);
|
|
if (lenW > 1.f) scaleWidth = float(inpShape[3] - 1) / (lenW - 1.f);
|
|
}
|
|
|
|
auto kind = outputs_arr.kind();
|
|
Mat out_;
|
|
UMat uout_;
|
|
if (kind == _InputArray::STD_VECTOR_MAT) {
|
|
std::vector<Mat>& outputs = outputs_arr.getMatVecRef();
|
|
outputs[0].fit(fold1d ? outShape1d : outShape, inp_.type());
|
|
out_ = fold1d ? outputs[0].reshape(1, outShape) : outputs[0];
|
|
|
|
if (outShape == inpShape)
|
|
{
|
|
inp_.copyTo(out_);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
CV_Assert(kind == _InputArray::STD_VECTOR_UMAT);
|
|
std::vector<UMat>& u_outputs = outputs_arr.getUMatVecRef();
|
|
u_outputs[0].fit(fold1d ? outShape1d : outShape, inp_.type());
|
|
uout_ = fold1d ? u_outputs[0].reshape(1, outShape) : u_outputs[0];
|
|
if (outShape == inpShape)
|
|
{
|
|
inp_.copyTo(uout_);
|
|
return;
|
|
}
|
|
out_.create(outShape, inp_.type());
|
|
}
|
|
|
|
int depth = inp_.type(), orig_depth = depth;
|
|
|
|
// Bilinear/cubic/antialias have no fixed-point CV_32S path yet.
|
|
if (depth == CV_32S && interpolation != "nearest") {
|
|
CV_Error(Error::StsNotImplemented,
|
|
"Resize2: CV_32S is currently only supported with nearest-neighbor "
|
|
"interpolation; bilinear/cubic/antialias would need a fixed-point "
|
|
"implementation to preserve int32 precision");
|
|
}
|
|
|
|
Mat inp, out;
|
|
if (depth != CV_32F && depth != CV_8S && depth != CV_8U && depth != CV_16F && depth != CV_16BF &&
|
|
depth != CV_32S) {
|
|
inp_.convertTo(inp, CV_32F);
|
|
out.fit(outShape, CV_32F);
|
|
depth = CV_32F;
|
|
} else {
|
|
inp = inp_;
|
|
out = out_;
|
|
}
|
|
|
|
if (antialias && inp.dims == 4 &&
|
|
(interpolation == "bilinear" || interpolation == "opencv_linear" || interpolation == "cubic"))
|
|
{
|
|
const bool cubic = (interpolation == "cubic");
|
|
float xsH, xsW;
|
|
if (!scales.empty()) {
|
|
int hIdx, wIdx;
|
|
spatialIndices(scales.size(), hIdx, wIdx);
|
|
xsH = scales[hIdx]; xsW = scales[wIdx];
|
|
} else {
|
|
xsH = float(outShape[2]) / inpShape[2];
|
|
xsW = float(outShape[3]) / inpShape[3];
|
|
}
|
|
switch (depth) {
|
|
case CV_8S: resizeAntialias<int8_t>(inp, out, xsH, xsW, cubic, cubicCoeffA, coordTransModeE); break;
|
|
case CV_8U: resizeAntialias<uint8_t>(inp, out, xsH, xsW, cubic, cubicCoeffA, coordTransModeE); break;
|
|
case CV_16F: resizeAntialias<hfloat>(inp, out, xsH, xsW, cubic, cubicCoeffA, coordTransModeE); break;
|
|
case CV_16BF: resizeAntialias<bfloat>(inp, out, xsH, xsW, cubic, cubicCoeffA, coordTransModeE); break;
|
|
case CV_32F: resizeAntialias<float>(inp, out, xsH, xsW, cubic, cubicCoeffA, coordTransModeE); break;
|
|
default: CV_Error(Error::StsUnsupportedFormat, "Unsupported depth");
|
|
}
|
|
}
|
|
else if(interpolation=="nearest"){
|
|
switch(depth){
|
|
case CV_8S:
|
|
resizeNearest<int8_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,nearestModeE,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_8U:
|
|
resizeNearest<uint8_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,nearestModeE,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_16F:
|
|
resizeNearest<hfloat>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,nearestModeE,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_16BF:
|
|
resizeNearest<bfloat>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,nearestModeE,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_32F:
|
|
resizeNearest<float>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,nearestModeE,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_32S:
|
|
resizeNearest<int32_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,nearestModeE,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
default: CV_Error(Error::StsUnsupportedFormat,"Unsupported depth");
|
|
}
|
|
}
|
|
else if(interpolation=="bilinear"||interpolation=="opencv_linear"){
|
|
switch(depth){
|
|
case CV_8S:
|
|
resizeBilinear<int8_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_8U:
|
|
resizeBilinear<uint8_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_16F:
|
|
resizeBilinear<hfloat>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_16BF:
|
|
resizeBilinear<bfloat>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_32F:
|
|
resizeBilinear<float>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
default: CV_Error(Error::StsUnsupportedFormat,"Unsupported depth");
|
|
}
|
|
}
|
|
else if(interpolation=="cubic"){
|
|
switch (depth) {
|
|
case CV_8S:
|
|
resizeCubic<int8_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,cubicCoeffA,excludeOutside,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_8U:
|
|
resizeCubic<uint8_t>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,cubicCoeffA,excludeOutside,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_16F:
|
|
resizeCubic<hfloat>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,cubicCoeffA,excludeOutside,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_16BF:
|
|
resizeCubic<bfloat>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,cubicCoeffA,excludeOutside,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
case CV_32F:
|
|
resizeCubic<float>(inp,out,scaleHeight,scaleWidth,length_resized_y,length_resized_x,cubicCoeffA,excludeOutside,coordTransMode,halfPixelCenters,roi_start_y,roi_end_y,roi_start_x,roi_end_x,extrapolation_value);
|
|
break;
|
|
default:
|
|
CV_Error(Error::StsUnsupportedFormat, "Unsupported depth");
|
|
}
|
|
}
|
|
else
|
|
CV_Error(Error::StsNotImplemented,"Unknown interpolation: "+interpolation);
|
|
|
|
if (orig_depth != depth) {
|
|
if (!uout_.empty())
|
|
out.convertTo(uout_, orig_depth);
|
|
else
|
|
out.convertTo(out_, orig_depth);
|
|
}
|
|
else if (!uout_.empty()) {
|
|
out.copyTo(uout_);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_CANN
|
|
virtual Ptr<BackendNode> initCann(const std::vector<Ptr<BackendWrapper> > &inputs,
|
|
const std::vector<Ptr<BackendWrapper> > &outputs,
|
|
const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
|
|
{
|
|
auto x = inputs[0].dynamicCast<CannBackendWrapper>();
|
|
auto x_desc = x->getTensorDesc();
|
|
auto op_x = nodes[0].dynamicCast<CannBackendNode>()->getOp();
|
|
auto output_y_desc = std::make_shared<ge::TensorDesc>(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT);
|
|
|
|
// create operator
|
|
if (interpolation == "nearest")
|
|
{
|
|
auto op = std::make_shared<ge::op::ResizeNearestNeighborV2>(name);
|
|
|
|
// set attributes
|
|
op->set_attr_align_corners(alignCorners);
|
|
op->set_attr_half_pixel_centers(halfPixelCenters);
|
|
|
|
// set inputs : x
|
|
op->set_input_x_by_name(*op_x, x->name.c_str());
|
|
op->update_input_desc_x(*x_desc);
|
|
// set inputs : size
|
|
std::vector<int> shape_of_size_mat{2};
|
|
std::vector<int> size_vec{outHeight, outWidth};
|
|
Mat size_mat(shape_of_size_mat, CV_32S, size_vec.data());
|
|
auto op_const_size = std::make_shared<CannConstOp>(size_mat.data, size_mat.type(), shape_of_size_mat, cv::format("%s_size", name.c_str()));
|
|
op->set_input_size(*(op_const_size->getOp()));
|
|
op->update_input_desc_size(*(op_const_size->getTensorDesc()));
|
|
|
|
// set outputs
|
|
op->update_output_desc_y(*output_y_desc);
|
|
|
|
return Ptr<BackendNode>(new CannBackendNode(op));
|
|
}
|
|
else if (interpolation == "opencv_linear" || interpolation == "bilinear")
|
|
{
|
|
auto op = std::make_shared<ge::op::ResizeBilinearV2D>(name);
|
|
|
|
// set attributes
|
|
op->set_attr_align_corners(alignCorners);
|
|
op->set_attr_half_pixel_centers(halfPixelCenters);
|
|
std::vector<int64_t> taget_size{(int64_t)outHeight, (int64_t)outWidth};
|
|
op->set_attr_size(taget_size);
|
|
|
|
// set inputs : x
|
|
op->set_input_x_by_name(*op_x, x->name.c_str());
|
|
op->update_input_desc_x(*x_desc);
|
|
|
|
// set outputs
|
|
op->update_output_desc_y(*output_y_desc);
|
|
|
|
return Ptr<BackendNode>(new CannBackendNode(op));
|
|
}
|
|
else
|
|
CV_Error(Error::StsNotImplemented, "Unsupported interpolation by CANN backend: " + interpolation);
|
|
}
|
|
#endif // HAVE_CANN
|
|
|
|
#ifdef HAVE_DNN_NGRAPH
|
|
virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inputs,
|
|
const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
|
|
{
|
|
auto& ieInpNode = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
|
|
|
|
ov::op::v4::Interpolate::InterpolateAttrs attrs;
|
|
|
|
if (interpolation == "nearest") {
|
|
attrs.mode = ov::op::v4::Interpolate::InterpolateMode::NEAREST;
|
|
attrs.coordinate_transformation_mode = ov::op::v4::Interpolate::CoordinateTransformMode::HALF_PIXEL;
|
|
} else if (interpolation == "bilinear") {
|
|
attrs.mode = ov::op::v4::Interpolate::InterpolateMode::LINEAR_ONNX;
|
|
attrs.coordinate_transformation_mode = ov::op::v4::Interpolate::CoordinateTransformMode::ASYMMETRIC;
|
|
} else {
|
|
CV_Error(Error::StsNotImplemented, format("Unsupported interpolation: %s", interpolation.c_str()));
|
|
}
|
|
attrs.shape_calculation_mode = ov::op::v4::Interpolate::ShapeCalcMode::SIZES;
|
|
|
|
CV_Assert(!halfPixelCenters || !alignCorners);
|
|
if (halfPixelCenters) {
|
|
attrs.coordinate_transformation_mode = ov::op::v4::Interpolate::CoordinateTransformMode::HALF_PIXEL;
|
|
} else if (alignCorners) {
|
|
attrs.coordinate_transformation_mode = ov::op::v4::Interpolate::CoordinateTransformMode::ALIGN_CORNERS;
|
|
}
|
|
|
|
attrs.nearest_mode = ov::op::v4::Interpolate::NearestMode::ROUND_PREFER_FLOOR;
|
|
|
|
|
|
std::vector<int64_t> shape = {outHeight, outWidth};
|
|
auto out_shape = std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{2}, shape.data());
|
|
|
|
auto& input_shape = ieInpNode.get_shape();
|
|
CV_Assert_N(input_shape[2] != 0, input_shape[3] != 0);
|
|
std::vector<float> scales = {static_cast<float>(outHeight) / input_shape[2], static_cast<float>(outWidth) / input_shape[3]};
|
|
auto scales_shape = std::make_shared<ov::op::v0::Constant>(ov::element::f32, ov::Shape{2}, scales.data());
|
|
|
|
auto axes = std::make_shared<ov::op::v0::Constant>(ov::element::i64, ov::Shape{2}, std::vector<int64_t>{2, 3});
|
|
auto interp = std::make_shared<ov::op::v4::Interpolate>(ieInpNode, out_shape, scales_shape, axes, attrs);
|
|
return Ptr<BackendNode>(new InfEngineNgraphNode(interp));
|
|
}
|
|
#endif // HAVE_DNN_NGRAPH
|
|
|
|
|
|
#ifdef HAVE_CUDA
|
|
Ptr<BackendNode> initCUDA(
|
|
void *context_,
|
|
const std::vector<Ptr<BackendWrapper>>& inputs,
|
|
const std::vector<Ptr<BackendWrapper>>& outputs
|
|
) override
|
|
{
|
|
auto context = reinterpret_cast<csl::CSLContext*>(context_);
|
|
|
|
cuda4dnn::ResizeConfiguration config;
|
|
if (interpolation == "nearest")
|
|
{
|
|
config.type = InterpolationType::NEAREST_NEIGHBOUR;
|
|
config.align_corners = alignCorners;
|
|
config.half_pixel_centers = halfPixelCenters;
|
|
}
|
|
else if (interpolation == "bilinear")
|
|
{
|
|
config.type = InterpolationType::BILINEAR;
|
|
config.align_corners = alignCorners;
|
|
config.half_pixel_centers = halfPixelCenters;
|
|
}
|
|
else if (interpolation == "opencv_linear")
|
|
{
|
|
config.type = InterpolationType::BILINEAR;
|
|
config.align_corners = false;
|
|
config.half_pixel_centers = true;
|
|
}
|
|
else
|
|
CV_Error(Error::StsNotImplemented, "Requested interpolation mode is not available in resize layer.");
|
|
return make_cuda_node<cuda4dnn::ResizeOp>(preferableTarget, std::move(context->stream), config);
|
|
}
|
|
#endif
|
|
|
|
protected:
|
|
int outWidth, outHeight;
|
|
const float zoomFactorWidth, zoomFactorHeight;
|
|
String interpolation;
|
|
float scaleWidth, scaleHeight;
|
|
bool alignCorners;
|
|
bool dynamicROI;
|
|
bool halfPixelCenters;
|
|
String coordTransMode;
|
|
CoordTransMode coordTransModeE;
|
|
NearestMode nearestModeE; // ONNX "nearest_mode" attribute
|
|
bool excludeOutside; // ONNX attribute for cubic
|
|
float cubicCoeffA;
|
|
float roi_start_y, roi_end_y, roi_start_x, roi_end_x;
|
|
float extrapolation_value; // Extrapolation value for tf_crop_and_resize mode
|
|
std::vector<int> axesAttr; // ONNX "axes" attribute (subset of dims that sizes/scales refer to)
|
|
String keepAspectPolicy; // ONNX "keep_aspect_ratio_policy": stretch|not_larger|not_smaller
|
|
bool antialias; // ONNX "antialias" attribute (filter stretching when downsampling)
|
|
};
|
|
|
|
Ptr<Resize2Layer> Resize2Layer::create(const LayerParams& params)
|
|
{
|
|
return Ptr<Resize2Layer>(new Resize2LayerImpl(params));
|
|
}
|
|
|
|
} // namespace dnn
|
|
} // namespace cv
|