2020-11-05 16:41:56 +08:00
|
|
|
//
|
|
|
|
// GeometryCrop.cpp
|
|
|
|
// MNN
|
|
|
|
//
|
|
|
|
// Created by MNN on 2020/04/22.
|
|
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "geometry/GeometryComputer.hpp"
|
|
|
|
#include "core/OpCommonUtils.hpp"
|
|
|
|
namespace MNN {
|
|
|
|
|
2021-11-30 10:10:53 +08:00
|
|
|
static int computeOffsetRegion(Tensor::InsideDescribe::NativeInsideDescribe* outputDes, Tensor* input, Tensor* output, Tensor* real,
|
2023-02-15 10:30:27 +08:00
|
|
|
const std::vector<int>& offsets,
|
|
|
|
const std::vector<int>& rightOffsets,
|
|
|
|
std::vector<int>& seperateInputDims,
|
2020-11-05 16:41:56 +08:00
|
|
|
std::vector<int>& seperateOutputDims, std::vector<int>& seperateOffsets,
|
|
|
|
std::vector<int>& seperateInputStrides, std::vector<int>& seperateOutputStrides,
|
2021-11-30 10:10:53 +08:00
|
|
|
int* remainStride,
|
|
|
|
int& remainStrideSize
|
|
|
|
) {
|
2020-11-05 16:41:56 +08:00
|
|
|
int currentInput = 1;
|
|
|
|
int currentOutput = 1;
|
2023-02-15 10:30:27 +08:00
|
|
|
int currentSize = 1;
|
2020-11-05 16:41:56 +08:00
|
|
|
auto inputDim = input->dimensions();
|
2023-02-15 10:30:27 +08:00
|
|
|
std::vector<int> seperateRightOffset;
|
2020-11-05 16:41:56 +08:00
|
|
|
for (int i = 0; i < inputDim; ++i) {
|
|
|
|
if (output->length(i) != input->length(i)) {
|
|
|
|
if (1 < currentInput) {
|
|
|
|
seperateInputDims.emplace_back(currentInput);
|
|
|
|
seperateOutputDims.emplace_back(currentOutput);
|
|
|
|
seperateOffsets.emplace_back(0);
|
2023-02-15 10:30:27 +08:00
|
|
|
seperateRightOffset.emplace_back(0);
|
2020-11-05 16:41:56 +08:00
|
|
|
}
|
|
|
|
seperateInputDims.emplace_back(input->length(i));
|
|
|
|
seperateOutputDims.emplace_back(output->length(i));
|
|
|
|
seperateOffsets.emplace_back(offsets[i]);
|
2023-02-15 10:30:27 +08:00
|
|
|
seperateRightOffset.emplace_back(rightOffsets[i]);
|
2020-11-05 16:41:56 +08:00
|
|
|
currentInput = 1;
|
|
|
|
currentOutput = 1;
|
2023-02-15 10:30:27 +08:00
|
|
|
currentSize = 1;
|
2020-11-05 16:41:56 +08:00
|
|
|
} else {
|
|
|
|
currentInput *= input->length(i);
|
|
|
|
currentOutput *= output->length(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (currentOutput != 1 || currentInput != 1) {
|
|
|
|
seperateInputDims.emplace_back(currentInput);
|
|
|
|
seperateOutputDims.emplace_back(currentOutput);
|
|
|
|
seperateOffsets.emplace_back(0);
|
2023-02-15 10:30:27 +08:00
|
|
|
seperateRightOffset.emplace_back(0);
|
2020-11-05 16:41:56 +08:00
|
|
|
}
|
|
|
|
seperateOutputStrides.resize(seperateOutputDims.size());
|
|
|
|
seperateInputStrides.resize(seperateOutputDims.size());
|
|
|
|
OpCommonUtils::computeStride(seperateOutputStrides.data(), seperateOutputDims.data(), seperateOutputDims.size());
|
|
|
|
OpCommonUtils::computeStride(seperateInputStrides.data(), seperateInputDims.data(), seperateInputDims.size());
|
|
|
|
|
|
|
|
int remainDimSize = seperateOffsets.size() > 3 ? (int)seperateOffsets.size() - 3 : 0;
|
2021-11-30 10:10:53 +08:00
|
|
|
remainStrideSize = remainDimSize;
|
|
|
|
int remainSize = OpCommonUtils::computeStride(remainStride, seperateOutputDims.data(), remainDimSize);
|
2023-02-15 10:30:27 +08:00
|
|
|
outputDes->regions.clear();
|
|
|
|
outputDes->regions.reserve(remainSize);
|
2020-11-05 16:41:56 +08:00
|
|
|
outputDes->memoryType = Tensor::InsideDescribe::MEMORY_VIRTUAL;
|
|
|
|
|
2021-11-30 10:10:53 +08:00
|
|
|
int cords[MNN_MAX_TENSOR_DIM];
|
2020-11-05 16:41:56 +08:00
|
|
|
for (int index = 0; index < remainSize; ++index) {
|
|
|
|
OpCommonUtils::unravelIndexHelper(cords, remainStride, remainDimSize, index);
|
2023-02-15 10:30:27 +08:00
|
|
|
bool valid = true;
|
|
|
|
for (int i = 0; i < remainDimSize; ++i) {
|
|
|
|
if (seperateOffsets[i] + cords[i] < 0) {
|
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cords[i] - seperateRightOffset[i] >= seperateOutputDims[i]) {
|
|
|
|
valid = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!valid) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Tensor::InsideDescribe::Region reg;
|
2020-11-05 16:41:56 +08:00
|
|
|
reg.src.offset = 0;
|
|
|
|
reg.dst.offset = 0;
|
|
|
|
for (int i = 0; i < remainDimSize; ++i) {
|
|
|
|
reg.src.offset += ((cords[i] + seperateOffsets[i]) * seperateInputStrides[i]);
|
|
|
|
reg.dst.offset += (cords[i] * seperateOutputStrides[i]);
|
|
|
|
}
|
2023-02-15 10:30:27 +08:00
|
|
|
MNN_ASSERT(reg.src.offset >= 0);
|
2020-11-05 16:41:56 +08:00
|
|
|
reg.origin = real;
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
|
|
auto match = (int)seperateOffsets.size() - i - 1;
|
|
|
|
if (match < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-02-15 10:30:27 +08:00
|
|
|
int size = seperateOutputDims[match];
|
|
|
|
if (seperateOffsets[match] >=0 ) {
|
|
|
|
reg.src.offset += seperateOffsets[match] * seperateInputStrides[match];
|
|
|
|
} else {
|
|
|
|
reg.dst.offset += (-seperateOffsets[match]) * seperateOutputStrides[match];
|
|
|
|
size = size + seperateOffsets[match];
|
|
|
|
}
|
|
|
|
if (seperateRightOffset[match] < 0) {
|
|
|
|
size = size + seperateRightOffset[match];
|
|
|
|
}
|
|
|
|
reg.size[3 - i - 1] = size;
|
2020-11-05 16:41:56 +08:00
|
|
|
reg.src.stride[3 - i - 1] = seperateInputStrides[match];
|
|
|
|
reg.dst.stride[3 - i - 1] = seperateOutputStrides[match];
|
|
|
|
}
|
2023-02-15 10:30:27 +08:00
|
|
|
MNN_ASSERT(reg.src.offset >= 0);
|
|
|
|
outputDes->regions.emplace_back(std::move(reg));
|
2020-11-05 16:41:56 +08:00
|
|
|
}
|
|
|
|
return remainSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
class GeometryCrop : public GeometryComputer {
|
|
|
|
public:
|
|
|
|
virtual bool onCompute(const Op* op, const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
|
|
|
|
Context& context, CommandBuffer& res) const override {
|
|
|
|
auto input = inputs[0];
|
|
|
|
auto cropParam = op->main_as_Crop();
|
|
|
|
auto axis = cropParam->axis();
|
|
|
|
int offsetSize = cropParam->offset()->size();
|
|
|
|
auto offsetData = cropParam->offset()->data();
|
|
|
|
const int inputDim = input->buffer().dimensions;
|
|
|
|
if (axis < 0) {
|
|
|
|
axis = inputDim + axis;
|
|
|
|
}
|
|
|
|
MNN_ASSERT(inputDim > 0);
|
|
|
|
std::vector<int> offsets(inputDim, 0);
|
2023-02-15 10:30:27 +08:00
|
|
|
std::vector<int> rightOffset(inputDim, 0);
|
2020-11-05 16:41:56 +08:00
|
|
|
for (int i = 0; i < inputDim; ++i) {
|
|
|
|
int cropOffset = 0;
|
|
|
|
if (i >= axis) {
|
|
|
|
if (offsetSize == 1) {
|
|
|
|
cropOffset = offsetData[0];
|
|
|
|
} else if (offsetSize > 1) {
|
|
|
|
cropOffset = offsetData[i - axis];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
offsets[i] = cropOffset;
|
|
|
|
}
|
|
|
|
std::vector<int> seperateInputDims;
|
|
|
|
std::vector<int> seperateOutputDims;
|
|
|
|
std::vector<int> seperateOffsets;
|
|
|
|
std::vector<int> seperateOutputStrides;
|
|
|
|
std::vector<int> seperateInputStrides;
|
2021-11-30 10:10:53 +08:00
|
|
|
int remainStride[MNN_MAX_TENSOR_DIM];
|
|
|
|
int remainStrideSize;
|
2023-02-15 10:30:27 +08:00
|
|
|
computeOffsetRegion(TensorUtils::getDescribe(outputs[0]), input, outputs[0], input, offsets, rightOffset, seperateInputDims,
|
2020-11-05 16:41:56 +08:00
|
|
|
seperateOutputDims, seperateOffsets, seperateInputStrides, seperateOutputStrides,
|
2021-11-30 10:10:53 +08:00
|
|
|
remainStride, remainStrideSize);
|
2020-11-05 16:41:56 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class GeometryPad : public GeometryComputer {
|
|
|
|
public:
|
|
|
|
virtual bool onCompute(const Op* op, const std::vector<Tensor*>& inputs, const std::vector<Tensor*>& outputs,
|
|
|
|
Context& context, CommandBuffer& res) const override {
|
|
|
|
auto input = inputs[0];
|
|
|
|
auto output = outputs[0];
|
|
|
|
auto outputDes = TensorUtils::getDescribe(output);
|
|
|
|
outputDes->regions.clear();
|
|
|
|
outputDes->memoryType = Tensor::InsideDescribe::MEMORY_VIRTUAL;
|
|
|
|
for (int i=0; i<input->dimensions(); ++i) {
|
|
|
|
if (input->length(i) == 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto paddings = inputs[1];
|
|
|
|
auto paddingPtr = paddings->host<int32_t>();
|
|
|
|
auto dimensions = input->dimensions();
|
|
|
|
std::vector<int> pads(dimensions);
|
2023-02-15 10:30:27 +08:00
|
|
|
std::vector<int> padRights(dimensions);
|
2020-11-05 16:41:56 +08:00
|
|
|
for (int i = 0; i < dimensions; ++i) {
|
|
|
|
pads[i] = paddingPtr[2 * i];
|
2023-02-15 10:30:27 +08:00
|
|
|
padRights[i] = paddingPtr[2 * i + 1];
|
2020-11-05 16:41:56 +08:00
|
|
|
}
|
|
|
|
auto param = op->main_as_PadParam();
|
|
|
|
std::vector<int> seperateInputDims;
|
|
|
|
std::vector<int> seperateOutputDims;
|
|
|
|
std::vector<int> seperateOffsets;
|
|
|
|
std::vector<int> seperateOutputStrides;
|
|
|
|
std::vector<int> seperateInputStrides;
|
2021-11-30 10:10:53 +08:00
|
|
|
int remainStride[MNN_MAX_TENSOR_DIM];
|
|
|
|
int remainStrideSize;
|
2020-11-05 16:41:56 +08:00
|
|
|
|
2023-02-15 10:30:27 +08:00
|
|
|
computeOffsetRegion(outputDes, output, input, input, pads, padRights, seperateOutputDims, seperateInputDims,
|
2021-11-30 10:10:53 +08:00
|
|
|
seperateOffsets, seperateOutputStrides, seperateInputStrides, remainStride, remainStrideSize);
|
2020-11-05 16:41:56 +08:00
|
|
|
int remainSize =
|
2021-11-30 10:10:53 +08:00
|
|
|
OpCommonUtils::computeStride(remainStride, seperateOutputDims.data(), remainStrideSize);
|
2020-11-05 16:41:56 +08:00
|
|
|
|
|
|
|
// Revert region
|
|
|
|
for (auto& reg : outputDes->regions) {
|
|
|
|
auto t = reg.dst;
|
|
|
|
reg.dst = reg.src;
|
|
|
|
reg.src = t;
|
|
|
|
}
|
|
|
|
auto mode = PadValueMode_CONSTANT;
|
|
|
|
if (param) {
|
|
|
|
mode = param->mode();
|
|
|
|
}
|
2021-04-28 18:02:10 +08:00
|
|
|
auto padInput = input;
|
|
|
|
int sStrideDiff = 1;
|
2020-11-05 16:41:56 +08:00
|
|
|
if (PadValueMode_CONSTANT == mode) {
|
2021-04-28 18:02:10 +08:00
|
|
|
if (inputs.size() <= 2) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Check Zero for inputs[2]
|
|
|
|
bool zero = false;
|
|
|
|
auto type = inputs[2]->getType();
|
2023-09-05 16:00:46 +08:00
|
|
|
if (!TensorUtils::getDescribe(inputs[2])->isMutable && inputs[2]->deviceId() == 0) {
|
|
|
|
switch (type.code) {
|
|
|
|
case halide_type_int:
|
|
|
|
{
|
|
|
|
if (type.bits == 8) {
|
|
|
|
zero = inputs[2]->host<int8_t>()[0] == 0;
|
|
|
|
} else if (type.bits == 32) {
|
|
|
|
zero = inputs[2]->host<int32_t>()[0] == 0;
|
|
|
|
}
|
2021-04-28 18:02:10 +08:00
|
|
|
}
|
2023-09-05 16:00:46 +08:00
|
|
|
break;
|
|
|
|
case halide_type_uint:
|
|
|
|
{
|
|
|
|
if (type.bits == 8) {
|
|
|
|
zero = inputs[2]->host<uint8_t>()[0] == 0;
|
|
|
|
} else if (type.bits == 32) {
|
|
|
|
zero = inputs[2]->host<uint32_t>()[0] == 0;
|
|
|
|
}
|
2021-04-28 18:02:10 +08:00
|
|
|
}
|
2023-09-05 16:00:46 +08:00
|
|
|
break;
|
|
|
|
case halide_type_float:
|
|
|
|
{
|
|
|
|
zero = inputs[2]->host<float>()[0] == 0.0f;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2021-04-28 18:02:10 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (zero) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
padInput = inputs[2];
|
|
|
|
sStrideDiff = 0;
|
2020-11-05 16:41:56 +08:00
|
|
|
}
|
|
|
|
// For Reflect and Mirror
|
|
|
|
/* Ref: https://www.tensorflow.org/api_docs/python/tf/pad
|
|
|
|
If mode is "REFLECT"
|
|
|
|
then both paddings[D, 0] and paddings[D, 1] must be no greater than tensor.dim_size(D) - 1.
|
|
|
|
If mode is "SYMMETRIC"
|
|
|
|
then both paddings[D, 0] and paddings[D, 1] must be no greater than tensor.dim_size(D).*/
|
|
|
|
int extraSub = 0;
|
|
|
|
if (PadValueMode_REFLECT == mode) {
|
|
|
|
extraSub = 1;
|
|
|
|
}
|
|
|
|
std::vector<int> rightPads(seperateOffsets.size());
|
|
|
|
for (int i = 0; i < rightPads.size(); ++i) {
|
|
|
|
rightPads[i] = seperateOutputDims[i] - seperateInputDims[i] - seperateOffsets[i];
|
|
|
|
}
|
|
|
|
std::vector<int> padRegion;
|
2021-11-30 10:10:53 +08:00
|
|
|
for (int i = remainStrideSize; i < seperateInputStrides.size(); ++i) {
|
2020-11-05 16:41:56 +08:00
|
|
|
// 0: center, 1: left, 2: right
|
|
|
|
int r = 1;
|
|
|
|
if (seperateOffsets[i] > 0) {
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
if (rightPads[i] > 0) {
|
|
|
|
r++;
|
|
|
|
}
|
|
|
|
padRegion.emplace_back(r);
|
|
|
|
}
|
2021-11-30 10:10:53 +08:00
|
|
|
int padRegionMod[MNN_MAX_TENSOR_DIM];
|
|
|
|
int regionSize = OpCommonUtils::computeStride(padRegionMod, padRegion.data(), padRegion.size());
|
|
|
|
int remainDimOffset = (int)remainStrideSize;
|
2020-11-05 16:41:56 +08:00
|
|
|
std::vector<int> padCord(padRegion.size());
|
2021-11-30 10:10:53 +08:00
|
|
|
std::vector<int> cords(remainStrideSize);
|
2020-11-05 16:41:56 +08:00
|
|
|
for (int pos = 0; pos < remainSize; ++pos) {
|
|
|
|
int dstBasicOffset = 0;
|
|
|
|
int srcBasicOffset = 0;
|
2021-11-30 10:10:53 +08:00
|
|
|
OpCommonUtils::unravelIndexHelper(cords.data(), remainStride, remainDimOffset, pos);
|
2020-11-05 16:41:56 +08:00
|
|
|
for (int i = 0; i < cords.size(); ++i) {
|
|
|
|
// cords is the pos of output
|
|
|
|
dstBasicOffset += cords[i] * seperateOutputStrides[i];
|
|
|
|
// compute cords for input
|
|
|
|
int inputPos = cords[i] - seperateOffsets[i];
|
|
|
|
if (inputPos >= seperateInputDims[i]) {
|
|
|
|
// last -> last - extraSub - 1
|
|
|
|
inputPos = (seperateInputDims[i] - inputPos) + seperateInputDims[i] - extraSub - 1;
|
|
|
|
}
|
|
|
|
if (inputPos < 0) {
|
|
|
|
// -1 -> 0 + extraSub
|
|
|
|
inputPos = -inputPos + 1 + extraSub;
|
|
|
|
}
|
|
|
|
srcBasicOffset += inputPos * seperateInputStrides[i];
|
|
|
|
}
|
|
|
|
for (int index = 1; index < regionSize; ++index) {
|
|
|
|
int dstOffset = dstBasicOffset;
|
|
|
|
int srcOffset = srcBasicOffset;
|
2021-11-30 10:10:53 +08:00
|
|
|
OpCommonUtils::unravelIndexHelper(padCord.data(), padRegionMod, padRegion.size(), index);
|
2020-11-05 16:41:56 +08:00
|
|
|
Tensor::InsideDescribe::Region region;
|
2021-04-28 18:02:10 +08:00
|
|
|
region.origin = padInput;
|
2020-11-05 16:41:56 +08:00
|
|
|
int sizeOffset = 3 - (int)padRegion.size();
|
|
|
|
for (int i = 0; i < padRegion.size(); ++i) {
|
|
|
|
int di = sizeOffset + i;
|
|
|
|
int si = remainDimOffset + i;
|
|
|
|
switch (padCord[i]) {
|
|
|
|
case 0:
|
|
|
|
// center part: dst: start(offset) -> src: 0
|
|
|
|
dstOffset += seperateOffsets[si] * seperateOutputStrides[si];
|
|
|
|
region.size[di] = seperateInputDims[si];
|
|
|
|
region.src.stride[di] = seperateInputStrides[si];
|
|
|
|
region.dst.stride[di] = seperateOutputStrides[si];
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
// right part: dst: start + inputDim -> src: inputDim - 1 - extra
|
|
|
|
dstOffset += (seperateOffsets[si] + seperateInputDims[si]) * seperateOutputStrides[si];
|
|
|
|
srcOffset += (seperateInputDims[si] - 1 - extraSub) * seperateInputStrides[si];
|
2022-05-06 19:51:20 +08:00
|
|
|
#define SET_SIZE(dst, size) \
|
|
|
|
if (mode == PadValueMode_REFLECT || mode == PadValueMode_SYMMETRIC) { \
|
|
|
|
if (size > seperateInputDims[si] - extraSub) { \
|
|
|
|
MNN_ERROR("padding size is too large, result is undefined!\n(padding <= dim - 1) on REFLECT mode, (padding <= dim) on SYMMETRIC mode\n"); \
|
|
|
|
} \
|
|
|
|
dst = ALIMIN(size, seperateInputDims[si] - extraSub);\
|
|
|
|
} else { dst = size; }
|
|
|
|
SET_SIZE(region.size[di], rightPads[si])
|
2020-11-05 16:41:56 +08:00
|
|
|
region.src.stride[di] = -seperateInputStrides[si];
|
|
|
|
region.dst.stride[di] = seperateOutputStrides[si];
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// offset = 0 means right part, offset > 0 means left part
|
|
|
|
if (seperateOffsets[si] > 0) {
|
|
|
|
// left part: dst: 0 -> src: seperateOffsets + extra - 1
|
|
|
|
auto srcPos = seperateOffsets[si] - 1 + extraSub;
|
2022-01-04 10:50:40 +08:00
|
|
|
if (mode == PadValueMode_EDGE) {
|
|
|
|
srcPos = 0;
|
|
|
|
}
|
2020-11-05 16:41:56 +08:00
|
|
|
srcOffset += srcPos * seperateInputStrides[si];
|
2022-05-06 19:51:20 +08:00
|
|
|
SET_SIZE(region.size[di], seperateOffsets[si])
|
2020-11-05 16:41:56 +08:00
|
|
|
region.src.stride[di] = -seperateInputStrides[si];
|
|
|
|
region.dst.stride[di] = seperateOutputStrides[si];
|
|
|
|
} else {
|
|
|
|
// right part: dst: start + inputDim -> src: inputDim - 1 - extra
|
|
|
|
dstOffset += (seperateOffsets[si] + seperateInputDims[si]) * seperateOutputStrides[si];
|
|
|
|
srcOffset += (seperateInputDims[si] - 1 - extraSub) * seperateInputStrides[si];
|
2022-05-06 19:51:20 +08:00
|
|
|
SET_SIZE(region.size[di], rightPads[si])
|
2020-11-05 16:41:56 +08:00
|
|
|
region.src.stride[di] = -seperateInputStrides[si];
|
|
|
|
region.dst.stride[di] = seperateOutputStrides[si];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2022-01-04 10:50:40 +08:00
|
|
|
if (padCord[i] != 0 && mode == PadValueMode_EDGE) {
|
|
|
|
region.src.stride[di] = 0;
|
|
|
|
}
|
2020-11-05 16:41:56 +08:00
|
|
|
}
|
|
|
|
region.src.offset = srcOffset;
|
|
|
|
region.dst.offset = dstOffset;
|
2021-04-28 18:02:10 +08:00
|
|
|
if (sStrideDiff == 0) {
|
|
|
|
region.src.offset = 0;
|
|
|
|
region.src.stride[0] = 0;
|
|
|
|
region.src.stride[1] = 0;
|
|
|
|
region.src.stride[2] = 0;
|
|
|
|
}
|
2020-11-05 16:41:56 +08:00
|
|
|
outputDes->regions.emplace_back(std::move(region));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void _create() {
|
|
|
|
std::shared_ptr<GeometryComputer> comp(new GeometryCrop);
|
|
|
|
GeometryComputer::registerGeometryComputer(comp, {OpType_Crop});
|
|
|
|
std::shared_ptr<GeometryComputer> comp2(new GeometryPad);
|
|
|
|
GeometryComputer::registerGeometryComputer(comp2, {OpType_Padding});
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_GEOMETRY(GeometryCrop, _create);
|
|
|
|
|
|
|
|
} // namespace MNN
|