// Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "lite/api/paddle_use_kernels.h" #include "lite/api/paddle_use_ops.h" #include "lite/core/arena/framework.h" #include "lite/core/tensor.h" namespace paddle { namespace lite { template void resize_bilinear_align(std::vector inputs, lite::Tensor* output) { int hin = inputs[0]->dims()[2]; int win = inputs[0]->dims()[3]; int channels = inputs[0]->dims()[1]; int num = inputs[0]->dims()[0]; int hout = output->dims()[2]; int wout = output->dims()[3]; dtype scale_w = static_cast(win - 1) / (wout - 1); dtype scale_h = static_cast(hin - 1) / (hout - 1); const dtype* src = inputs[0]->data(); dtype* dst = output->mutable_data(); int dst_stride_w = 1; int dst_stride_h = wout; int dst_stride_c = wout * hout; int dst_stride_batch = wout * hout * channels; int src_stride_w = 1; int src_stride_h = win; int src_stride_c = win * hin; int src_stride_batch = win * hin * channels; for (int n = 0; n < num; ++n) { for (int c = 0; c < channels; ++c) { int src_index = n * src_stride_batch + c * src_stride_c; for (int h = 0; h < hout; ++h) { for (int w = 0; w < wout; ++w) { dtype fw = w * scale_w; dtype fh = h * scale_h; int w_start = static_cast(fw); int w_id = w_start < win - 1 ? 1 : 0; int w_end = static_cast(fw + w_id); int h_start = static_cast(fh); int h_id = h_start < hin - 1 ? 1 : 0; int h_end = static_cast(fh + h_id); fw -= w_start; fh -= h_start; const dtype w00 = (1.0 - fh) * (1.0 - fw); const dtype w01 = fw * (1.0 - fh); const dtype w10 = fh * (1.0 - fw); const dtype w11 = fw * fh; dtype tl = src[src_index + w_start * src_stride_w + h_start * src_stride_h]; dtype tr = src[src_index + w_end * src_stride_w + h_start * src_stride_h]; dtype bl = src[src_index + w_start * src_stride_w + h_end * src_stride_h]; dtype br = src[src_index + w_end * src_stride_w + h_end * src_stride_h]; int dst_index = n * dst_stride_batch + c * dst_stride_c + h * dst_stride_h + w * dst_stride_w; dst[dst_index] = static_cast(w00 * tl + w01 * tr + w10 * bl + w11 * br); } } } } } template void resize_bilinear_no_align(std::vector inputs, lite::Tensor* output) { int hin = inputs[0]->dims()[2]; int win = inputs[0]->dims()[3]; int channels = inputs[0]->dims()[1]; int num = inputs[0]->dims()[0]; int hout = output->dims()[2]; int wout = output->dims()[3]; dtype scale_w = static_cast(win) / (wout); dtype scale_h = static_cast(hin) / (hout); const dtype* src = inputs[0]->data(); dtype* dst = output->mutable_data(); int dst_stride_w = 1; int dst_stride_h = wout; int dst_stride_c = wout * hout; int dst_stride_batch = wout * hout * channels; int src_stride_w = 1; int src_stride_h = win; int src_stride_c = win * hin; int src_stride_batch = win * hin * channels; for (int n = 0; n < num; ++n) { for (int c = 0; c < channels; ++c) { int src_index = n * src_stride_batch + c * src_stride_c; for (int h = 0; h < hout; ++h) { for (int w = 0; w < wout; ++w) { dtype fw = scale_w * (w + 0.5f) - 0.5f; fw = (fw < 0) ? 0 : fw; dtype fh = scale_h * (h + 0.5f) - 0.5f; fh = (fh < 0) ? 0 : fh; int w_start = static_cast(fw); int w_id = w_start < win - 1 ? 1 : 0; int w_end = static_cast(fw + w_id); int h_start = static_cast(fh); int h_id = h_start < hin - 1 ? 1 : 0; int h_end = static_cast(fh + h_id); fw -= w_start; fh -= h_start; const dtype w00 = (1.0 - fh) * (1.0 - fw); const dtype w01 = fw * (1.0 - fh); const dtype w10 = fh * (1.0 - fw); const dtype w11 = fw * fh; dtype tl = src[src_index + w_start * src_stride_w + h_start * src_stride_h]; dtype tr = src[src_index + w_end * src_stride_w + h_start * src_stride_h]; dtype bl = src[src_index + w_start * src_stride_w + h_end * src_stride_h]; dtype br = src[src_index + w_end * src_stride_w + h_end * src_stride_h]; int dst_index = n * dst_stride_batch + c * dst_stride_c + h * dst_stride_h + w * dst_stride_w; dst[dst_index] = static_cast(w00 * tl + w01 * tr + w10 * bl + w11 * br); } } } } } class BilinearInterpComputeTester : public arena::TestCase { protected: // common attributes for this op. std::string input0_ = "X"; std::string input1_ = "OutSize"; std::string output_ = "Out"; float height_scale_ = 0.f; float width_scale_ = 0.f; int out_height_ = -1; int out_width_ = -1; bool align_corners_ = true; std::string interp_method_ = "Bilinear"; DDim dims_{{1, 1}}; DDim _dims0_{{1, 1, 16, 16}}; DDim _dims1_{{2}}; public: BilinearInterpComputeTester(const Place& place, const std::string& alias, float height_scale, float width_scale, int out_height, int out_width, bool align_corners, std::string interp_method) : TestCase(place, alias), height_scale_(height_scale), width_scale_(width_scale), out_height_(out_height), out_width_(out_width), align_corners_(align_corners), interp_method_(interp_method) {} void RunBaseline(Scope* scope) override { width_scale_ = height_scale_; std::vector inputs; inputs.emplace_back(scope->FindTensor(input0_)); inputs.emplace_back(scope->FindTensor(input1_)); auto outsize_data = inputs[1]->data(); if (out_width_ != -1 && out_height_ != -1) { height_scale_ = static_cast(out_height_ / inputs[0]->dims()[2]); width_scale_ = static_cast(out_width_ / inputs[0]->dims()[3]); } auto* outputs = scope->NewTensor(output_); CHECK(outputs); if (inputs.size() > 1) { int h_out = outsize_data[0]; // HW int w_out = outsize_data[1]; // HW int num_cout = inputs[0]->dims()[0]; int c_cout = inputs[0]->dims()[1]; outputs->Resize({num_cout, c_cout, h_out, w_out}); } else { int out_h; int out_w; if (-1 == out_height_ && -1 == out_width_) { out_h = inputs[0]->dims()[2] * height_scale_; out_w = inputs[0]->dims()[3] * width_scale_; } else { out_h = out_height_; out_w = out_width_; } outputs->Resize( {inputs[0]->dims()[0], inputs[0]->dims()[1], out_h, out_w}); } if (align_corners_) { resize_bilinear_align(inputs, outputs); } else { resize_bilinear_no_align(inputs, outputs); } } void PrepareOpDesc(cpp::OpDesc* op_desc) { op_desc->SetType("bilinear_interp"); op_desc->SetInput("X", {input0_}); op_desc->SetInput("OutSize", {input1_}); op_desc->SetOutput("Out", {output_}); op_desc->SetAttr("scale", height_scale_); op_desc->SetAttr("out_h", out_height_); op_desc->SetAttr("out_w", out_width_); op_desc->SetAttr("align_corners", align_corners_); op_desc->SetAttr("interp_method", interp_method_); } void PrepareData() override { std::vector data0(_dims0_.production()); for (int i = 0; i < _dims0_.production(); i++) { data0[i] = i * 1.1; } SetCommonTensor(input0_, _dims0_, data0.data()); std::vector data1(_dims1_.production()); for (int i = 0; i < _dims1_.production(); i++) { data1[i] = 16; } SetCommonTensor(input1_, _dims1_, data1.data()); } }; void test_bilinear_interp(Place place) { std::string interp_method = "Bilinear"; for (float scale : {1., 0.5, 0.3}) { for (int out_height : {8, 16}) { for (int out_width : {8, 16}) { for (bool align_corners : {true, false}) { std::unique_ptr tester( new BilinearInterpComputeTester(place, "def", scale, scale, out_height, out_width, align_corners, interp_method)); arena::Arena arena(std::move(tester), place, 2e-5); arena.TestPrecision(); } } } } } TEST(BilinearInterp, precision) { // #ifdef LITE_WITH_X86 // Place place(TARGET(kX86)); // #endif #ifdef LITE_WITH_ARM Place place(TARGET(kARM)); test_bilinear_interp(place); #endif } } // namespace lite } // namespace paddle