GitOrigin-RevId: 0887656864
tags/v1.11.0
| @@ -56,7 +56,22 @@ public: | |||
| _megdnn_workspace workspace) { | |||
| exec(src, mat, {}, dst, workspace); | |||
| } | |||
| /** | |||
| * \param[in] srcs consists of n TensorNDs, each TensorND has shape (1, channel, | |||
| * in_height, in_width) \param[in] mat (n, 3, 3) \param[out] dst (n, channel, | |||
| * out_height, out_width) | |||
| * | |||
| * \note | |||
| * srcs and dst can have different shapes, as long as their c agree and the size of | |||
| * srcs is equal to n. every element of srcs, mat and dst should be contiguous. | |||
| * | |||
| * equivalent to: | |||
| * TensorND src{nullptr, TensorLayout({n, channel, in_height, in_width}, | |||
| * srcs[0].layout.dtype)}; auto concat = handle()->create_operator<Concat>(); | |||
| * concat->exec(srcs, src); | |||
| * auto warp = handle()->create_operator<WarpPerspectiveForward>(); | |||
| * warp->exec(src, mat, dst, workspace); | |||
| */ | |||
| void exec( | |||
| _megdnn_in const TensorNDArray& srcs, _megdnn_tensor_in mat, | |||
| _megdnn_tensor_out dst, _megdnn_workspace workspace) { | |||
| @@ -75,11 +90,25 @@ public: | |||
| virtual void exec( | |||
| _megdnn_tensor_in src, _megdnn_tensor_in mat, _megdnn_tensor_in mat_idx, | |||
| _megdnn_tensor_out dst, _megdnn_workspace workspace) = 0; | |||
| /** | |||
| * \p srcs should have m elements, and \p mat and \p mat_idx should | |||
| * both have batch size n. Each item in \p mat_idx must be in the range | |||
| * of [0, m-1]. | |||
| * | |||
| * \param mat_idx the indices of input image that each matrix in \p mat | |||
| * should act on. It can also be empty and in such case \p mat batch size | |||
| * should be the same as the number of elements in \p srcs . | |||
| */ | |||
| virtual void exec( | |||
| _megdnn_in const TensorNDArray& srcs, _megdnn_tensor_in mat, | |||
| _megdnn_tensor_in mat_idx, _megdnn_tensor_out dst, | |||
| _megdnn_workspace workspace) = 0; | |||
| _megdnn_workspace workspace) { | |||
| static_cast<void>(srcs); | |||
| static_cast<void>(mat); | |||
| static_cast<void>(mat_idx); | |||
| static_cast<void>(dst); | |||
| static_cast<void>(workspace); | |||
| } | |||
| size_t get_workspace_in_bytes( | |||
| const TensorLayout& src, const TensorLayout& mat, const TensorLayout& dst) { | |||
| @@ -98,7 +127,13 @@ public: | |||
| virtual size_t get_workspace_in_bytes( | |||
| const TensorLayoutArray& srcs, const TensorLayout& mat, | |||
| const TensorLayout& mat_idx, const TensorLayout& dst) = 0; | |||
| const TensorLayout& mat_idx, const TensorLayout& dst) { | |||
| static_cast<void>(srcs); | |||
| static_cast<void>(mat); | |||
| static_cast<void>(mat_idx); | |||
| static_cast<void>(dst); | |||
| return 0; | |||
| } | |||
| protected: | |||
| void check_exec( | |||
| @@ -10,12 +10,8 @@ void WarpPerspectiveBase::check_layout_fwd( | |||
| auto s = srcs.front(); | |||
| for (auto&& src : srcs) { | |||
| megdnn_assert_contiguous(src); | |||
| megdnn_assert(src.dtype == s.dtype); | |||
| megdnn_assert(src.ndim == s.ndim); | |||
| src.eq_layout(s); | |||
| megdnn_assert(src.shape[0] == 1); | |||
| for (size_t i = 0; i < s.ndim; i++) { | |||
| megdnn_assert(src.shape[i] == s.shape[i]); | |||
| } | |||
| megdnn_assert(src.format == s.format); | |||
| } | |||
| megdnn_assert_contiguous(mat); | |||
| @@ -289,7 +289,7 @@ TEST_F(FALLBACK, WARP_PERSPECTIVE_MULTI_SRC_WITH_IDX_NCHW) { | |||
| shapes.emplace_back(TensorShape{{idx, 3, 3}}); | |||
| checker.set_rng(bs, &rng); | |||
| // mat_idx | |||
| shapes.emplace_back(TensorShape{{idx}}); | |||
| shapes.emplace_back(TensorShape({idx})); | |||
| checker.set_dtype(bs + 1, dtype::Int32()); | |||
| idx_rng = UniformIntRNG{0, (int)bs - 1}; | |||
| checker.set_rng(bs + 1, &idx_rng); | |||
| @@ -338,7 +338,7 @@ TEST_F(FALLBACK, WARP_PERSPECTIVE_MULTI_SRC_WITH_IDX_NHWC) { | |||
| shapes.emplace_back(TensorShape{{idx, 3, 3}}); | |||
| checker.set_rng(bs, &rng); | |||
| // mat_idx | |||
| shapes.emplace_back(TensorShape{{idx}}); | |||
| shapes.emplace_back(TensorShape({idx})); | |||
| checker.set_dtype(bs + 1, dtype::Int32()); | |||
| idx_rng = UniformIntRNG{0, (int)bs - 1}; | |||
| checker.set_rng(bs + 1, &idx_rng); | |||
| @@ -1,6 +1,7 @@ | |||
| #include "megbrain/opr/imgproc.h" | |||
| #include "./internal/megdnn_opr_wrapper.inl" | |||
| #include "megbrain/graph/grad_impl.h" | |||
| #include "megbrain/opr/basic_arith.h" | |||
| #include "megbrain/opr/io.h" | |||
| #include "megbrain/opr/utility.h" | |||
| @@ -25,6 +26,26 @@ WarpPerspectiveForward::WarpPerspectiveForward( | |||
| outshape_by_symvar_enable(input().size() - 1, input().size() - 1); | |||
| } | |||
| WarpPerspectiveForward::WarpPerspectiveForward( | |||
| const VarNodeArrayView& srcs, VarNode* mat, VarNode* mat_idx, | |||
| VarNode* out_shape, const Param& param, const OperatorNodeConfig& config) | |||
| : Super(OperatorNodeBaseCtorParam{ | |||
| srcs[0]->owner_graph(), config, "warp_perspective", {srcs[0], mat}}) { | |||
| mgb_assert(!srcs.empty()); | |||
| m_is_multi_src = true; | |||
| m_srcs_size = srcs.size(); | |||
| init_megdnn_opr(*this, param); | |||
| for (auto&& src : srcs) { | |||
| add_input({src}); | |||
| } | |||
| if (mat_idx) { | |||
| add_input({mat, mat_idx, out_shape}); | |||
| } else { | |||
| add_input({mat, out_shape}); | |||
| } | |||
| outshape_by_symvar_enable(input().size() - 1, input().size() - 1); | |||
| } | |||
| SymbolVar WarpPerspectiveForward::make( | |||
| SymbolVar i0, SymbolVar i1, SymbolVar i2, SymbolVar i3, const Param& param, | |||
| const OperatorNodeConfig& config) { | |||
| @@ -32,6 +53,15 @@ SymbolVar WarpPerspectiveForward::make( | |||
| i0.node(), i1.node(), i2.node(), i3.node(), param, config); | |||
| } | |||
| SymbolVar WarpPerspectiveForward::make( | |||
| const VarNodeArrayView& i0, SymbolVar i1, SymbolVar i2, SymbolVar i3, | |||
| const Param& param, OperatorNodeConfig config) { | |||
| mgb_assert(!i0.empty()); | |||
| intl::BatchedDTypePromotion dtp{i0}; | |||
| return SymbolVar{i0[0]}.insert_single_output_opr<WarpPerspectiveForward>( | |||
| dtp.get_vars(), i1.node(), i2.node(), i3.node(), param, config); | |||
| } | |||
| void WarpPerspectiveForward::init_output_dtype() { | |||
| if (config().output_dtype().valid()) { | |||
| output(0)->dtype(config().output_dtype()); | |||
| @@ -48,63 +78,110 @@ void WarpPerspectiveForward::outshape_by_symvar_do_get_output_shape( | |||
| TensorShape& dest, const ShapeInferInfo& shpinfo) { | |||
| TensorShape oshp2d; | |||
| cg::copy_tensor_value_to_shape(oshp2d, *shpinfo.shpval_inp_val.at(0)); | |||
| auto imgshp = shpinfo.shape_inp_shp.at(0), matshp = shpinfo.shape_inp_shp.at(1); | |||
| mgb_assert( | |||
| (imgshp.ndim == 4 || imgshp.ndim == 5) && matshp.ndim == 3 && | |||
| oshp2d.ndim == 2 && matshp.shape[1] == 3 && matshp.shape[2] == 3, | |||
| "shape mismatch for WarpPerspectiveForward: img=%s mat=%s " | |||
| "out2d=%s", | |||
| imgshp.to_string().c_str(), matshp.to_string().c_str(), | |||
| oshp2d.to_string().c_str()); | |||
| if (input().size() == 3) { | |||
| mgb_assert( | |||
| imgshp[0] == matshp[0], "batchsize mismatch: img=%zu mat=%zu", | |||
| imgshp[0], matshp[0]); | |||
| } else { | |||
| mgb_assert(input().size() == 4); | |||
| auto mat_idx_shp = shpinfo.shape_inp_shp.at(2); | |||
| TensorShape imgshp, matshp, mat_idx_shp; | |||
| TensorShapeArray imgshps; | |||
| if (!m_is_multi_src) { | |||
| imgshp = shpinfo.shape_inp_shp.at(0); | |||
| matshp = shpinfo.shape_inp_shp.at(1); | |||
| mgb_assert( | |||
| mat_idx_shp[0] == matshp[0] && mat_idx_shp.ndim == 1, | |||
| "invalid mat_idx shape: mat=%zu mat_idx=%s", matshp[0], | |||
| mat_idx_shp.to_string().c_str()); | |||
| } | |||
| (imgshp.ndim == 4 || imgshp.ndim == 5) && matshp.ndim == 3 && | |||
| oshp2d.ndim == 2 && matshp.shape[1] == 3 && | |||
| matshp.shape[2] == 3, | |||
| "shape mismatch for WarpPerspectiveForward: img=%s mat=%s " | |||
| "out2d=%s", | |||
| imgshp.to_string().c_str(), matshp.to_string().c_str(), | |||
| oshp2d.to_string().c_str()); | |||
| if (input().size() == 3) { | |||
| mgb_assert( | |||
| imgshp[0] == matshp[0], "batchsize mismatch: img=%zu mat=%zu", | |||
| imgshp[0], matshp[0]); | |||
| } else { | |||
| mgb_assert(input().size() == 4); | |||
| mat_idx_shp = shpinfo.shape_inp_shp.at(2); | |||
| mgb_assert( | |||
| mat_idx_shp[0] == matshp[0] && mat_idx_shp.ndim == 1, | |||
| "invalid mat_idx shape: mat=%zu mat_idx=%s", matshp[0], | |||
| mat_idx_shp.to_string().c_str()); | |||
| } | |||
| switch (param().format) { | |||
| case Param::Format::NCHW_NCHW4_IC_SMALL: | |||
| case Param::Format::NHWC_NCHW4_IC_SMALL: | |||
| dest.ndim = 5; | |||
| dest[0] = matshp[0]; | |||
| dest.shape[1] = 1; | |||
| dest.shape[2] = oshp2d.shape[0]; | |||
| dest.shape[3] = oshp2d.shape[1]; | |||
| dest.shape[4] = 4; | |||
| break; | |||
| case Param::Format::NHWC_NCHW: | |||
| dest.ndim = 4; | |||
| dest[0] = matshp[0]; | |||
| dest.shape[1] = imgshp.shape[3]; | |||
| dest.shape[2] = oshp2d.shape[0]; | |||
| dest.shape[3] = oshp2d.shape[1]; | |||
| break; | |||
| default: | |||
| size_t height_idx = 0; | |||
| if (param().format == Param::Format::NCHW || | |||
| param().format == Param::Format::NCHW4 || | |||
| param().format == Param::Format::NCHW64) { | |||
| height_idx = 2; | |||
| } else { | |||
| height_idx = 1; | |||
| } | |||
| dest = imgshp; | |||
| dest[0] = matshp[0]; | |||
| if (param().format == Param::Format::NHWCD4) { | |||
| dest.shape[height_idx] = oshp2d.shape[0]; | |||
| dest.shape[height_idx + 2] = oshp2d.shape[1]; | |||
| } else { | |||
| for (int i = 0; i < 2; ++i) | |||
| dest.shape[height_idx + i] = oshp2d.shape[i]; | |||
| switch (param().format) { | |||
| case Param::Format::NCHW_NCHW4_IC_SMALL: | |||
| case Param::Format::NHWC_NCHW4_IC_SMALL: | |||
| dest.ndim = 5; | |||
| dest[0] = matshp[0]; | |||
| dest.shape[1] = 1; | |||
| dest.shape[2] = oshp2d.shape[0]; | |||
| dest.shape[3] = oshp2d.shape[1]; | |||
| dest.shape[4] = 4; | |||
| break; | |||
| case Param::Format::NHWC_NCHW: | |||
| dest.ndim = 4; | |||
| dest[0] = matshp[0]; | |||
| dest.shape[1] = imgshp.shape[3]; | |||
| dest.shape[2] = oshp2d.shape[0]; | |||
| dest.shape[3] = oshp2d.shape[1]; | |||
| break; | |||
| default: | |||
| size_t height_idx = 0; | |||
| if (param().format == Param::Format::NCHW || | |||
| param().format == Param::Format::NCHW4 || | |||
| param().format == Param::Format::NCHW64) { | |||
| height_idx = 2; | |||
| } else { | |||
| height_idx = 1; | |||
| } | |||
| dest = imgshp; | |||
| dest[0] = matshp[0]; | |||
| if (param().format == Param::Format::NHWCD4) { | |||
| dest.shape[height_idx] = oshp2d.shape[0]; | |||
| dest.shape[height_idx + 2] = oshp2d.shape[1]; | |||
| } else { | |||
| for (int i = 0; i < 2; ++i) | |||
| dest.shape[height_idx + i] = oshp2d.shape[i]; | |||
| } | |||
| break; | |||
| } | |||
| } else { | |||
| imgshp = shpinfo.shape_inp_shp.at(0); | |||
| matshp = shpinfo.shape_inp_shp.at(m_srcs_size); | |||
| for (size_t i = 0; i < m_srcs_size; i++) { | |||
| imgshps.emplace_back(shpinfo.shape_inp_shp.at(i)); | |||
| mgb_assert(imgshps[i].ndim == imgshp.ndim); | |||
| for (size_t j = 0; j < imgshp.ndim; j++) { | |||
| mgb_assert(imgshps[i].shape[j] == imgshp[j]); | |||
| } | |||
| break; | |||
| } | |||
| mgb_assert( | |||
| imgshp[0] == 1 && imgshp.ndim == 4 && matshp.ndim == 3 && | |||
| oshp2d.ndim == 2 && matshp.shape[1] == 3 && | |||
| matshp.shape[2] == 3, | |||
| "shape mismatch for WarpPerspectiveForward: img=%s mat=%s " | |||
| "out2d=%s", | |||
| imgshp.to_string().c_str(), matshp.to_string().c_str(), | |||
| oshp2d.to_string().c_str()); | |||
| if (input().size() - m_srcs_size == 2) { | |||
| mgb_assert( | |||
| m_srcs_size == matshp[0], "batchsize mismatch: img=%zu mat=%zu", | |||
| m_srcs_size, matshp[0]); | |||
| } else { | |||
| mgb_assert(input().size() - m_srcs_size == 3); | |||
| mat_idx_shp = shpinfo.shape_inp_shp.at(m_srcs_size + 1); | |||
| mgb_assert( | |||
| mat_idx_shp[0] == matshp[0] && mat_idx_shp.ndim == 1, | |||
| "invalid mat_idx shape: mat=%zu mat_idx=%s", matshp[0], | |||
| mat_idx_shp.to_string().c_str()); | |||
| } | |||
| size_t height_idx = 0; | |||
| if (param().format == Param::Format::NCHW) { | |||
| height_idx = 2; | |||
| } else { | |||
| height_idx = 1; | |||
| } | |||
| dest = imgshp; | |||
| dest[0] = matshp[0]; | |||
| for (int i = 0; i < 2; ++i) | |||
| dest.shape[height_idx + i] = oshp2d.shape[i]; | |||
| } | |||
| } | |||
| @@ -114,22 +191,61 @@ void WarpPerspectiveForward::init_output_static_infer_desc() { | |||
| } | |||
| void WarpPerspectiveForward::scn_do_execute() { | |||
| if (input().size() == 3) { | |||
| intl::_MegDNNOprMethInvoker<2, 1>::exec(megdnn_opr(), this); | |||
| if (!m_is_multi_src) { | |||
| if (input().size() == 3) { | |||
| intl::_MegDNNOprMethInvoker<2, 1>::exec(megdnn_opr(), this); | |||
| } else { | |||
| intl::_MegDNNOprMethInvoker<3, 1>::exec(megdnn_opr(), this); | |||
| } | |||
| } else { | |||
| intl::_MegDNNOprMethInvoker<3, 1>::exec(megdnn_opr(), this); | |||
| megdnn::TensorNDArray srcs; | |||
| for (size_t i = 0; i < m_srcs_size; i++) { | |||
| srcs.push_back(input(i)->dev_tensor().as_megdnn()); | |||
| } | |||
| if (input().size() - m_srcs_size == 2) { | |||
| megdnn_opr()->exec( | |||
| srcs, input(m_srcs_size)->dev_tensor().as_megdnn(), | |||
| output(0)->dev_tensor().as_megdnn(), | |||
| intl::get_megdnn_workspace_from_var(output().back())); | |||
| } else { | |||
| megdnn_opr()->exec( | |||
| srcs, input(m_srcs_size)->dev_tensor().as_megdnn(), | |||
| input(m_srcs_size + 1)->dev_tensor().as_megdnn(), | |||
| output(0)->dev_tensor().as_megdnn(), | |||
| intl::get_megdnn_workspace_from_var(output().back())); | |||
| } | |||
| } | |||
| } | |||
| size_t WarpPerspectiveForward::get_workspace_size_bytes( | |||
| const TensorShapeArray& input_shapes, | |||
| const TensorShapeArray& output_shapes) const { | |||
| if (input().size() == 3) { | |||
| return intl::_MegDNNOprMethInvoker<2, 1>::get_workspace_in_bytes( | |||
| megdnn_opr(), this, input_shapes, output_shapes); | |||
| if (!m_is_multi_src) { | |||
| if (input().size() == 3) { | |||
| return intl::_MegDNNOprMethInvoker<2, 1>::get_workspace_in_bytes( | |||
| megdnn_opr(), this, input_shapes, output_shapes); | |||
| } else { | |||
| return intl::_MegDNNOprMethInvoker<3, 1>::get_workspace_in_bytes( | |||
| megdnn_opr(), this, input_shapes, output_shapes); | |||
| } | |||
| } else { | |||
| return intl::_MegDNNOprMethInvoker<3, 1>::get_workspace_in_bytes( | |||
| megdnn_opr(), this, input_shapes, output_shapes); | |||
| TensorLayoutArray srcs; | |||
| for (size_t i = 0; i < m_srcs_size; i++) { | |||
| srcs.push_back(TensorLayout{ | |||
| input_shapes[i], input(i)->dtype(), input(i)->format()}); | |||
| } | |||
| TensorLayout mat{ | |||
| input_shapes[m_srcs_size], input(m_srcs_size)->dtype(), | |||
| input(m_srcs_size)->format()}; | |||
| TensorLayout dst{output_shapes[0], output(0)->dtype(), output(0)->format()}; | |||
| if (input().size() - m_srcs_size == 2) { | |||
| return megdnn_opr()->get_workspace_in_bytes(srcs, mat, dst); | |||
| } else { | |||
| TensorLayout mat_idx{ | |||
| input_shapes[m_srcs_size + 1], input(m_srcs_size + 1)->dtype(), | |||
| input(m_srcs_size + 1)->format()}; | |||
| return megdnn_opr()->get_workspace_in_bytes(srcs, mat, mat_idx, dst); | |||
| } | |||
| } | |||
| } | |||
| @@ -19,10 +19,34 @@ struct OprMaker<opr::WarpPerspective, 0> { | |||
| .node() | |||
| ->owner_opr(); | |||
| } else { | |||
| mgb_assert(inputs.size() == 4); | |||
| return Opr::make(inputs[0], inputs[1], inputs[2], inputs[3], param, config) | |||
| .node() | |||
| ->owner_opr(); | |||
| bool with_mat_idx = false; | |||
| VarNodeArray inps = inputs; | |||
| VarNode *mat, *mat_idx, *outshp; | |||
| outshp = inps.back(); | |||
| inps.pop_back(); | |||
| if (inps.back()->shape().ndim == 3) { | |||
| mat = inps.back(); | |||
| } else { | |||
| mat_idx = inps.back(); | |||
| inps.pop_back(); | |||
| mat = inps.back(); | |||
| with_mat_idx = true; | |||
| } | |||
| inps.pop_back(); | |||
| if (inps.size() == 1) { | |||
| mgb_assert(with_mat_idx); | |||
| return Opr::make( | |||
| inputs[0], inputs[1], inputs[2], inputs[3], param, | |||
| config) | |||
| .node() | |||
| ->owner_opr(); | |||
| } else if (with_mat_idx) { | |||
| return Opr::make(inps, mat, mat_idx, outshp, param, config) | |||
| .node() | |||
| ->owner_opr(); | |||
| } else { | |||
| return Opr::make(inps, mat, outshp, param, config).node()->owner_opr(); | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| @@ -31,6 +31,10 @@ public: | |||
| VarNode* in_tensor, VarNode* mat, VarNode* mat_idx, VarNode* out_shape, | |||
| const Param& param, const OperatorNodeConfig& config); | |||
| WarpPerspectiveForward( | |||
| const VarNodeArrayView& in_tensor, VarNode* mat, VarNode* mat_idx, | |||
| VarNode* out_shape, const Param& param, const OperatorNodeConfig& config); | |||
| MGE_WIN_DECLSPEC_FUC static SymbolVar make( | |||
| SymbolVar in_tensor, SymbolVar mat, SymbolVar mat_idx, SymbolVar out_shape, | |||
| const Param& param = {}, const OperatorNodeConfig& config = {}); | |||
| @@ -49,6 +53,26 @@ public: | |||
| config); | |||
| } | |||
| MGE_WIN_DECLSPEC_FUC static SymbolVar make( | |||
| const VarNodeArrayView& in_tensor, SymbolVar mat, SymbolVar mat_idx, | |||
| SymbolVar out_shape, const Param& param = {}, | |||
| OperatorNodeConfig config = {}); | |||
| static SymbolVar make( | |||
| const VarNodeArrayView& in_tensor, SymbolVar mat, SymbolVar out_shape, | |||
| const Param& param = {}, const OperatorNodeConfig& config = {}) { | |||
| return make(in_tensor, mat, SymbolVar{}, out_shape, param, config); | |||
| } | |||
| static SymbolVar make( | |||
| const VarNodeArrayView& in_tensor, SymbolVar mat, | |||
| const TensorShape& out_shape, const Param& param = {}, | |||
| const OperatorNodeConfig& config = {}) { | |||
| return make( | |||
| in_tensor, mat, cg::var_from_tensor_shape(in_tensor[0], out_shape), | |||
| param, config); | |||
| } | |||
| private: | |||
| void init_output_dtype() override; | |||
| void add_input_layout_constraint() override; | |||
| @@ -62,6 +86,8 @@ private: | |||
| const TensorShapeArray& output_shapes) const override; | |||
| void record_execute_deps(ExecDependencyArray& deps) override; | |||
| bool m_is_multi_src = false; | |||
| size_t m_srcs_size = 0; | |||
| }; | |||
| using WarpPerspective = WarpPerspectiveForward; | |||
| @@ -89,6 +89,250 @@ TEST(TestOprImgproc, WarpPerspective) { | |||
| .run({TensorShape{N, C, 10, 9}, {N, 3, 3}}, opt); | |||
| } | |||
| TEST(TestOprImgproc, WarpPerspective_MultiSrc) { | |||
| set_rand_seed(20220801); // a seed that can pass the test | |||
| constexpr size_t INP_H = 6, INP_W = 4, N = 3, C = 3; | |||
| using Checker = AutoOprChecker<4, 1>; | |||
| TensorShape out_shp{N, C, 9, 10}; | |||
| auto make_graph = [&](const Checker::SymInpArray& inputs) -> Checker::SymOutArray { | |||
| SymbolVarArray srcs; | |||
| for (size_t i = 0; i < N; i++) { | |||
| srcs.push_back(inputs[i]); | |||
| } | |||
| return {opr::WarpPerspective::make( | |||
| srcs, inputs[N], TensorShape{out_shp.shape[2], out_shp.shape[3]})}; | |||
| }; | |||
| auto fwd = [&](Checker::NumOutArray& dest, Checker::NumInpArray inp) { | |||
| auto opr = megdnn_naive_handle()->create_operator<megdnn::WarpPerspective>(); | |||
| dest[0].resize(out_shp); | |||
| megdnn::TensorNDArray srcs; | |||
| for (size_t i = 0; i < N; i++) { | |||
| srcs.push_back(inp[i]->as_megdnn()); | |||
| } | |||
| opr->exec(srcs, inp[N]->as_megdnn(), dest[0].as_megdnn(), {}); | |||
| }; | |||
| auto dump_mat = [&](const Checker::NumInpArray& inp) -> std::string { | |||
| std::ostringstream ostr; | |||
| ostr << std::setprecision(3); | |||
| auto&& mat = *inp[N]; | |||
| mgb_assert(mat.shape().ndim == 3); | |||
| auto ptr = mat.ptr<float>(); | |||
| for (size_t n = 0; n < mat.shape().shape[0]; ++n) { | |||
| ostr << "mat " << n << ":\n"; | |||
| for (size_t i = 0; i < 3; ++i) { | |||
| for (size_t j = 0; j < 3; ++j) { | |||
| ostr << std::setw(10) << *(ptr++); | |||
| } | |||
| ostr << '\n'; | |||
| } | |||
| } | |||
| return ostr.str(); | |||
| }; | |||
| Checker::RunOptions opt; | |||
| opt.numdiff_eps_single_inp[1] = 1e-5; | |||
| opt.numdiff_max_err_single_inp[1] = 0.5; | |||
| Checker(make_graph, fwd) | |||
| .set_input_generator(N, warp_perspective_mat_gen(N, INP_H, INP_W)) | |||
| .set_input_dump_on_error(dump_mat) | |||
| .disable_grad_check() | |||
| .run({TensorShape{1, C, 10, 9}, {1, C, 10, 9}, {1, C, 10, 9}, {N, 3, 3}}, | |||
| opt) | |||
| .run({TensorShape{1, C, 4, 5}, {1, C, 4, 5}, {1, C, 4, 5}, {N, 3, 3}}, opt) | |||
| .run({TensorShape{1, C, 6, 5}, {1, C, 6, 5}, {1, C, 6, 5}, {N, 3, 3}}, opt); | |||
| } | |||
| TEST(TestOprImgproc, WarpPerspective_MultiSrc_NHWC) { | |||
| set_rand_seed(20220801); // a seed that can pass the test | |||
| opr::WarpPerspective::Param param; | |||
| param.format = opr::WarpPerspective::Param::Format::NHWC; | |||
| constexpr size_t INP_H = 6, INP_W = 4, N = 3, C = 3; | |||
| using Checker = AutoOprChecker<4, 1>; | |||
| TensorShape out_shp{N, 9, 10, C}; | |||
| auto make_graph = [&](const Checker::SymInpArray& inputs) -> Checker::SymOutArray { | |||
| SymbolVarArray srcs; | |||
| for (size_t i = 0; i < N; i++) { | |||
| srcs.push_back(inputs[i]); | |||
| } | |||
| return {opr::WarpPerspective::make( | |||
| srcs, inputs[N], TensorShape{out_shp.shape[1], out_shp.shape[2]}, | |||
| param)}; | |||
| }; | |||
| auto fwd = [&](Checker::NumOutArray& dest, Checker::NumInpArray inp) { | |||
| auto opr = megdnn_naive_handle()->create_operator<megdnn::WarpPerspective>(); | |||
| opr->param() = param; | |||
| dest[0].resize(out_shp); | |||
| megdnn::TensorNDArray srcs; | |||
| for (size_t i = 0; i < N; i++) { | |||
| srcs.push_back(inp[i]->as_megdnn()); | |||
| } | |||
| opr->exec(srcs, inp[N]->as_megdnn(), dest[0].as_megdnn(), {}); | |||
| }; | |||
| Checker::RunOptions opt; | |||
| opt.numdiff_eps_single_inp[1] = 1e-5; | |||
| opt.numdiff_max_err_single_inp[1] = 0.5; | |||
| Checker(make_graph, fwd) | |||
| .set_input_generator(N, warp_perspective_mat_gen(N, INP_H, INP_W)) | |||
| .disable_grad_check() | |||
| .run({TensorShape{1, 10, 9, C}, {1, 10, 9, C}, {1, 10, 9, C}, {N, 3, 3}}, | |||
| opt) | |||
| .run({TensorShape{1, 4, 5, C}, {1, 4, 5, C}, {1, 4, 5, C}, {N, 3, 3}}, opt) | |||
| .run({TensorShape{1, 6, 5, C}, {1, 6, 5, C}, {1, 6, 5, C}, {N, 3, 3}}, opt); | |||
| } | |||
| TEST(TestOprImgproc, WarpPerspectiveWithMatIdx_MultiSrc) { | |||
| constexpr size_t INP_H = 13, INP_W = 9, N_MAT = 23, N_SRC = 3, C = 3; | |||
| std::mt19937 rng(next_rand_seed()); | |||
| auto rand_real = [&](double lo, double hi) { | |||
| return rng() / (std::mt19937::max() + 1.0) * (hi - lo) + lo; | |||
| }; | |||
| auto rand_real2 = [&](double range) { return rand_real(-range, range); }; | |||
| using Checker = AutoOprChecker<5, 1>; | |||
| TensorShape out_shp{N_MAT, C, 9, 10}; | |||
| auto make_graph = [&](const Checker::SymInpArray& inputs) -> Checker::SymOutArray { | |||
| SymbolVarArray srcs; | |||
| for (size_t i = 0; i < N_SRC; i++) { | |||
| srcs.push_back(inputs[i]); | |||
| } | |||
| return {opr::WarpPerspective::make( | |||
| srcs, inputs[N_SRC], inputs[N_SRC + 1], | |||
| cg::var_from_tensor_shape( | |||
| srcs[0], {out_shp.shape[2], out_shp.shape[3]}))}; | |||
| }; | |||
| auto fwd = [&](Checker::NumOutArray& dest, Checker::NumInpArray inp) { | |||
| auto opr = megdnn_naive_handle()->create_operator<megdnn::WarpPerspective>(); | |||
| dest[0].resize(out_shp); | |||
| megdnn::TensorNDArray srcs; | |||
| for (size_t i = 0; i < N_SRC; i++) { | |||
| srcs.push_back(inp[i]->as_megdnn()); | |||
| } | |||
| opr->exec( | |||
| srcs, inp[N_SRC]->as_megdnn(), inp[N_SRC + 1]->as_megdnn(), | |||
| dest[0].as_megdnn(), {}); | |||
| }; | |||
| auto gen_mat = [&](HostTensorND& mat) { | |||
| auto ptr = mat.ptr<float>(); | |||
| for (size_t i = 0; i < N_MAT; ++i) { | |||
| auto rot = rand_real(0, M_PI * 2), scale = rand_real(0.8, 1.2), | |||
| sheer = rand_real(0.9, 1.1), dy = rand_real2(INP_H * 0.5), | |||
| dx = rand_real2(INP_W * 0.5), ky = rand_real2(0.1 / INP_H), | |||
| kx = rand_real2(0.1 / INP_W), kb = rand_real2(0.1) + 1; | |||
| ptr[0] = ptr[4] = cos(rot) * scale; | |||
| ptr[1] = -(ptr[3] = sin(rot) * scale); | |||
| ptr[3] *= sheer; | |||
| ptr[4] *= sheer; | |||
| ptr[2] = dx; | |||
| ptr[5] = dy; | |||
| ptr[6] = kx; | |||
| ptr[7] = ky; | |||
| ptr[8] = kb; | |||
| ptr += 9; | |||
| } | |||
| mgb_assert(ptr == mat.ptr<float>() + mat.shape().total_nr_elems()); | |||
| }; | |||
| HostTensorGenerator<dtype::Int32> gen_mat_idx_rng{0, N_SRC}; | |||
| auto gen_mat_idx = [&](HostTensorND& mat) { mat = *gen_mat_idx_rng(mat.shape()); }; | |||
| Checker(make_graph, fwd) | |||
| .set_input_generator(N_SRC, gen_mat) | |||
| .set_input_generator(N_SRC + 1, gen_mat_idx) | |||
| .set_input_dtype(N_SRC + 1, dtype::Int32{}) | |||
| .disable_grad_check() | |||
| .run({TensorShape{1, C, 4, 5}, | |||
| {1, C, 4, 5}, | |||
| {1, C, 4, 5}, | |||
| {N_MAT, 3, 3}, | |||
| {N_MAT}}) | |||
| .run({TensorShape{1, C, 6, 5}, | |||
| {1, C, 6, 5}, | |||
| {1, C, 6, 5}, | |||
| {N_MAT, 3, 3}, | |||
| {N_MAT}}) | |||
| .run({TensorShape{1, C, 22, 19}, | |||
| {1, C, 22, 19}, | |||
| {1, C, 22, 19}, | |||
| {N_MAT, 3, 3}, | |||
| {N_MAT}}); | |||
| } | |||
| TEST(TestOprImgproc, WarpPerspectiveWithMatIdx_MultiSrc_NHWC) { | |||
| constexpr size_t INP_H = 13, INP_W = 9, N_MAT = 23, N_SRC = 3, C = 3; | |||
| opr::WarpPerspective::Param param; | |||
| param.format = opr::WarpPerspective::Param::Format::NHWC; | |||
| std::mt19937 rng(next_rand_seed()); | |||
| auto rand_real = [&](double lo, double hi) { | |||
| return rng() / (std::mt19937::max() + 1.0) * (hi - lo) + lo; | |||
| }; | |||
| auto rand_real2 = [&](double range) { return rand_real(-range, range); }; | |||
| using Checker = AutoOprChecker<5, 1>; | |||
| TensorShape out_shp{N_MAT, 9, 10, C}; | |||
| auto make_graph = [&](const Checker::SymInpArray& inputs) -> Checker::SymOutArray { | |||
| SymbolVarArray srcs; | |||
| for (size_t i = 0; i < N_SRC; i++) { | |||
| srcs.push_back(inputs[i]); | |||
| } | |||
| return {opr::WarpPerspective::make( | |||
| srcs, inputs[N_SRC], inputs[N_SRC + 1], | |||
| cg::var_from_tensor_shape( | |||
| srcs[0], {out_shp.shape[1], out_shp.shape[2]}), | |||
| param)}; | |||
| }; | |||
| auto fwd = [&](Checker::NumOutArray& dest, Checker::NumInpArray inp) { | |||
| auto opr = megdnn_naive_handle()->create_operator<megdnn::WarpPerspective>(); | |||
| opr->param() = param; | |||
| dest[0].resize(out_shp); | |||
| megdnn::TensorNDArray srcs; | |||
| for (size_t i = 0; i < N_SRC; i++) { | |||
| srcs.push_back(inp[i]->as_megdnn()); | |||
| } | |||
| opr->exec( | |||
| srcs, inp[N_SRC]->as_megdnn(), inp[N_SRC + 1]->as_megdnn(), | |||
| dest[0].as_megdnn(), {}); | |||
| }; | |||
| auto gen_mat = [&](HostTensorND& mat) { | |||
| auto ptr = mat.ptr<float>(); | |||
| for (size_t i = 0; i < N_MAT; ++i) { | |||
| auto rot = rand_real(0, M_PI * 2), scale = rand_real(0.8, 1.2), | |||
| sheer = rand_real(0.9, 1.1), dy = rand_real2(INP_H * 0.5), | |||
| dx = rand_real2(INP_W * 0.5), ky = rand_real2(0.1 / INP_H), | |||
| kx = rand_real2(0.1 / INP_W), kb = rand_real2(0.1) + 1; | |||
| ptr[0] = ptr[4] = cos(rot) * scale; | |||
| ptr[1] = -(ptr[3] = sin(rot) * scale); | |||
| ptr[3] *= sheer; | |||
| ptr[4] *= sheer; | |||
| ptr[2] = dx; | |||
| ptr[5] = dy; | |||
| ptr[6] = kx; | |||
| ptr[7] = ky; | |||
| ptr[8] = kb; | |||
| ptr += 9; | |||
| } | |||
| mgb_assert(ptr == mat.ptr<float>() + mat.shape().total_nr_elems()); | |||
| }; | |||
| HostTensorGenerator<dtype::Int32> gen_mat_idx_rng{0, N_SRC}; | |||
| auto gen_mat_idx = [&](HostTensorND& mat) { mat = *gen_mat_idx_rng(mat.shape()); }; | |||
| Checker(make_graph, fwd) | |||
| .set_input_generator(N_SRC, gen_mat) | |||
| .set_input_generator(N_SRC + 1, gen_mat_idx) | |||
| .set_input_dtype(N_SRC + 1, dtype::Int32{}) | |||
| .disable_grad_check() | |||
| .run({TensorShape{1, 4, 5, C}, | |||
| {1, 4, 5, C}, | |||
| {1, 4, 5, C}, | |||
| {N_MAT, 3, 3}, | |||
| {N_MAT}}) | |||
| .run({TensorShape{1, 6, 5, C}, | |||
| {1, 6, 5, C}, | |||
| {1, 6, 5, C}, | |||
| {N_MAT, 3, 3}, | |||
| {N_MAT}}) | |||
| .run({TensorShape{1, 22, 19, C}, | |||
| {1, 22, 19, C}, | |||
| {1, 22, 19, C}, | |||
| {N_MAT, 3, 3}, | |||
| {N_MAT}}); | |||
| } | |||
| TEST(TestOprImgproc, WarpPerspective_NCHW4) { | |||
| set_rand_seed(19931102); | |||
| constexpr size_t INP_H = 6, INP_W = 4, N = 2, C = 12; | |||