Files
2026-07-13 13:33:03 +08:00

211 lines
8.3 KiB
C++

//
// GridSampleGrad.cpp
// MNN
//
// Created by MNN on 2022/09/07.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include "OpGrad.hpp"
using namespace std;
namespace MNN {
using namespace MNN::Express;
class GridSampleGrad : public OpGrad {
public:
virtual std::vector<Express::VARP> onGrad(Express::EXPRP expr,
const std::vector<Express::VARP>& backwardOutput) override {
auto op = expr->get();
const auto& inputs = expr->inputs();
std::vector<VARP> res(inputs.size());
#ifndef GRIDSAMPLER_GRAD_DONT_CREATENEWOP
std::unique_ptr<MNN::OpT> gradOp(op->UnPack());
gradOp->name.clear();
gradOp->main.AsGridSample()->backward = true;
auto gradForParameterExpr = Expr::create(gradOp.get(), {backwardOutput[0], inputs[1], _Shape(inputs[0], NCHW)});
res[0] = Variable::create(gradForParameterExpr);
return res;
#else
auto param = op->main_as_GridSample();
auto sampleMode = param->mode();
auto padMode = param->paddingMode();
auto alignCorners = param->alignCorners();
auto input = inputs[0];
auto grid = inputs[1];
auto inputInfo = input->getInfo();
MNN_ASSERT(nullptr != inputInfo && (inputInfo->dim.size() == 4 || inputInfo->dim.size() == 5));
auto outputDiff = backwardOutput[0];
outputDiff = _Convert(outputDiff, NCHW);
auto diffInfo = outputDiff->getInfo();
const int inB = inputInfo->dim[0];
const int inC = inputInfo->dim[1];
const int inH = inputInfo->dim[2];
const int inW = inputInfo->dim[3];
const int outB = diffInfo->dim[0];
const int outC = diffInfo->dim[1];
const int outH = diffInfo->dim[2];
const int outW = diffInfo->dim[3];
auto one = _Scalar<float>(1.0f);
auto zeroFive = _Scalar<float>(0.5f);
std::vector<float> inShape = {float(inW), float(inH)};
auto inShapeVar = _Const((void*)inShape.data(), {1, 1, 1, 2}, NCHW);
outputDiff = _Transpose(outputDiff, {0, 2, 3, 1}); // NHWC
VARP sourceCord = nullptr;
if (alignCorners) {
sourceCord = (grid + one) * (inShapeVar - one) * zeroFive; // N, outH, outW, 2 (w, h)
} else {
sourceCord = ((grid + one) * inShapeVar - one) * zeroFive; // N, outH, outW, 2 (w, h)
}
if (sampleMode == SampleMode_NEAREST) {
auto indices = _Cast<int32_t>(_Floor(sourceCord + zeroFive)); // N, outH, outW, 2 (w, h)
{ // handle three pad modes
auto cords = _Split(indices, {1, 1}, -1);
auto wCord = cords[0];
auto hCord = cords[1];
auto zero = _Scalar<int>(0);
auto one = _Scalar<int>(1);
auto wVar = _Scalar<int>(inW);
auto hVar = _Scalar<int>(inH);
if (padMode == BorderMode_ZEROS) {
auto wMask1 = one - _Less(wCord, zero);
auto wMask2 = one - _GreaterEqual(wCord, wVar);
auto hMask1 = one - _Less(hCord, zero);
auto hMask2 = one - _GreaterEqual(hCord, hVar);
auto mask = wMask1 * wMask2 * hMask1 * hMask2;
outputDiff = outputDiff * _Cast<float>(mask);
}
wCord = Express::_Maximum(wCord, zero);
wCord = Express::_Minimum(wCord, wVar - one);
hCord = Express::_Maximum(hCord, zero);
hCord = Express::_Minimum(hCord, hVar - one);
indices = _Concat({wCord, hCord}, -1);
}
std::vector<int> bIndices;
for (int i = 0; i < outB; i++) {
for (int j = 0; j < outH*outW; j++) {
bIndices.push_back(i);
}
}
auto bIndicesVar = _Const((void*)bIndices.data(), {outB, outH, outW, 1}, NCHW, halide_type_of<int>());
indices = _Concat({bIndicesVar, indices}, -1);
auto updates = outputDiff;
std::vector<int> inputShape = {inB, inW, inH, inC};
auto shape = _Const(inputShape.data(), {4}, NCHW, halide_type_of<int>());
auto temp = _ScatterNd(indices, updates, shape, 0); // 0 for add
res[0] = _Transpose(temp, {0, 3, 2, 1}); // NCHW
} else if (sampleMode == SampleMode_BILINEAR) {
auto w0h0 = _Cast<int>(_Floor(sourceCord));
auto w1h1 = _Cast<int>(_Ceil(sourceCord));
auto factors0 = sourceCord - _Cast<float>(w0h0);
auto cords0 = _Split(w0h0, {1, 1}, -1);
auto w0 = cords0[0];
auto h0 = cords0[1];
auto cords1 = _Split(w1h1, {1, 1}, -1);
auto w1 = cords1[0];
auto h1 = cords1[1];
auto fs0 = _Split(factors0, {1, 1}, -1);
auto wf0 = fs0[0];
auto hf0 = fs0[1];
auto wf1 = _Scalar<float>(1.0f) - wf0;
auto hf1 = _Scalar<float>(1.0f) - hf0;
auto w00 = wf1 * hf1;
auto w01 = wf1 * hf0;
auto w10 = wf0 * hf1;
auto w11 = wf0 * hf0;
auto updates = outputDiff;
auto u00 = updates * w00;
auto u01 = updates * w01;
auto u10 = updates * w10;
auto u11 = updates * w11;
{ // handle three pad modes
auto zero = _Scalar<int>(0);
auto one = _Scalar<int>(1);
auto wVar = _Scalar<int>(inW);
auto hVar = _Scalar<int>(inH);
if (padMode == BorderMode_ZEROS) {
auto wMask0 = one - _Less(w0, zero);
auto wMask1 = one - _GreaterEqual(w0, wVar);
auto wMask2 = one - _Less(w1, zero);
auto wMask3 = one - _GreaterEqual(w1, wVar);
auto hMask0 = one - _Less(h0, zero);
auto hMask1 = one - _GreaterEqual(h0, hVar);
auto hMask2 = one - _Less(h1, zero);
auto hMask3 = one - _GreaterEqual(h1, hVar);
auto mask00 = wMask0 * wMask1 * hMask0 * hMask1;
auto mask01 = wMask0 * wMask1 * hMask2 * hMask3;
auto mask10 = wMask2 * wMask3 * hMask0 * hMask1;
auto mask11 = wMask2 * wMask3 * hMask2 * hMask3;
u00 = u00 * _Cast<float>(mask00);
u01 = u01 * _Cast<float>(mask01);
u10 = u10 * _Cast<float>(mask10);
u11 = u11 * _Cast<float>(mask11);
}
w0 = Express::_Maximum(w0, zero);
w0 = Express::_Minimum(w0, wVar - one);
w1 = Express::_Maximum(w1, zero);
w1 = Express::_Minimum(w1, wVar - one);
h0 = Express::_Maximum(h0, zero);
h0 = Express::_Minimum(h0, hVar - one);
h1 = Express::_Maximum(h1, zero);
h1 = Express::_Minimum(h1, hVar - one);
}
std::vector<int> bIndices;
for (int i = 0; i < outB; i++) {
for (int j = 0; j < outH*outW; j++) {
bIndices.push_back(i);
}
}
auto bIndicesVar = _Const((void*)bIndices.data(), {outB, outH, outW, 1}, NCHW, halide_type_of<int>());
auto bw0h1 = _Concat({bIndicesVar, w0, h1}, -1);
auto bw1h0 = _Concat({bIndicesVar, w1, h0}, -1);
auto bw0h0 = _Concat({bIndicesVar, w0, h0}, -1);
auto bw1h1 = _Concat({bIndicesVar, w1, h1}, -1);
std::vector<int> inputShape = {inB, inW, inH, inC};
auto shape = _Const(inputShape.data(), {4}, NCHW, halide_type_of<int>());
auto temp0 = _ScatterNd(bw0h0, u00, shape, 0); // 0 for add
auto temp1 = _ScatterNd(bw0h1, u01, shape, 0);
auto temp2 = _ScatterNd(bw1h0, u10, shape, 0);
auto temp3 = _ScatterNd(bw1h1, u11, shape, 0);
auto temp = temp0 + temp1 + temp2 + temp3;
res[0] = _Transpose(temp, {0, 3, 2, 1}); // NCHW
}
res[0] = _Convert(res[0], inputInfo->order);
return res;
#endif
}
};
static void _create() {
static GridSampleGrad _c;
OpGrad::insert((int)OpType_GridSample, &_c);
OpGrad::insert((int)OpType_Texture, &_c);
}
REGISTER_GRAD(GridSampleGrad_cpp, _create);
};