GitOrigin-RevId: 4540788660
tags/v0.5.0
@@ -6,7 +6,8 @@ | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#pragma once | |||
#include "megdnn/internal/opr_header_prologue.h" | |||
@@ -21,23 +22,23 @@ class FlipBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(FlipBase, OperatorBase); | |||
DEF_OPR_PARAM(Flip); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout &src, TensorLayout &dst); | |||
void check_layout_fwd(const TensorLayout &src, const TensorLayout &dst); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout& src, TensorLayout& dst); | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& dst); | |||
}; | |||
class FlipForward : public FlipBase { | |||
DEF_OPR_IMPL(FlipForward, FlipBase, 1, 1); | |||
public: | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout &src, TensorLayout &dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &dst) = 0; | |||
void deduce_layout(const TensorLayout& src, TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, const TensorLayout &dst, | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& dst, | |||
size_t workspace_in_bytes); | |||
}; | |||
using Flip = FlipForward; | |||
@@ -46,23 +47,23 @@ class RotateBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(RotateBase, OperatorBase); | |||
DEF_OPR_PARAM(Rotate); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout &src, TensorLayout &dst); | |||
void check_layout_fwd(const TensorLayout &src, const TensorLayout &dst); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout& src, TensorLayout& dst); | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& dst); | |||
}; | |||
class RotateForward : public RotateBase { | |||
DEF_OPR_IMPL(RotateForward, RotateBase, 1, 1); | |||
public: | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout &src, TensorLayout &dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &dst) = 0; | |||
void deduce_layout(const TensorLayout& src, TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, const TensorLayout &dst, | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& dst, | |||
size_t workspace_in_bytes); | |||
}; | |||
using Rotate = RotateForward; | |||
@@ -71,23 +72,23 @@ class ROICopyBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(ROICopyBase, OperatorBase); | |||
DEF_OPR_PARAM(ROICopy); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout &src, TensorLayout &dst); | |||
void check_layout_fwd(const TensorLayout &src, const TensorLayout &dst); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout& src, TensorLayout& dst); | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& dst); | |||
}; | |||
class ROICopyForward : public ROICopyBase { | |||
DEF_OPR_IMPL(ROICopyForward, ROICopyBase, 1, 1); | |||
public: | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout &src, TensorLayout &dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &dst) = 0; | |||
void deduce_layout(const TensorLayout& src, TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, const TensorLayout &dst, | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& dst, | |||
size_t workspace_in_bytes); | |||
}; | |||
using ROICopy = ROICopyForward; | |||
@@ -96,23 +97,23 @@ class CvtColorBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(CvtColorBase, OperatorBase); | |||
DEF_OPR_PARAM(CvtColor); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout &src, TensorLayout &dst); | |||
void check_layout_fwd(const TensorLayout &src, const TensorLayout &dst); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout& src, TensorLayout& dst); | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& dst); | |||
}; | |||
class CvtColorForward : public CvtColorBase { | |||
DEF_OPR_IMPL(CvtColorForward, CvtColorBase, 1, 1); | |||
public: | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout &src, TensorLayout &dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &dst) = 0; | |||
void deduce_layout(const TensorLayout& src, TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, const TensorLayout &dst, | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& dst, | |||
size_t workspace_in_bytes); | |||
}; | |||
using CvtColor = CvtColorForward; | |||
@@ -124,20 +125,21 @@ class WarpAffineBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(WarpAffineBase, OperatorBase); | |||
DEF_OPR_PARAM(WarpAffine); | |||
public: | |||
using InterpolationMode = Param::InterpolationMode; | |||
using BorderMode = Param::BorderMode; | |||
protected: | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& trans, | |||
const TensorLayout& dst); | |||
std::string param_msg() const; | |||
int get_real_coord(int p, int len); | |||
public: | |||
using InterpolationMode = Param::InterpolationMode; | |||
using BorderMode = Param::BorderMode; | |||
protected: | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& trans, | |||
const TensorLayout& dst); | |||
std::string param_msg() const; | |||
int get_real_coord(int p, int len); | |||
}; | |||
class WarpAffineForward : public WarpAffineBase { | |||
DEF_OPR_IMPL(WarpAffineForward, WarpAffineBase, 2, 1); | |||
public: | |||
public: | |||
/** | |||
* \param[in] src input tensor | |||
* \param[in] trans transform matrix tensor | |||
@@ -148,13 +150,13 @@ class WarpAffineForward : public WarpAffineBase { | |||
*/ | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_in trans, | |||
_megdnn_tensor_out dst, _megdnn_workspace workspace) = 0; | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &trans, | |||
const TensorLayout &dst) = 0; | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& trans, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, const TensorLayout &trans, | |||
const TensorLayout &dst, size_t workspace_in_bytes); | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& trans, | |||
const TensorLayout& dst, size_t workspace_in_bytes); | |||
}; | |||
using WarpAffine = WarpAffineForward; | |||
@@ -162,23 +164,23 @@ class GaussianBlurBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(GaussianBlurBase, OperatorBase); | |||
DEF_OPR_PARAM(GaussianBlur); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout &src, TensorLayout &dst); | |||
void check_layout_fwd(const TensorLayout &src, const TensorLayout &dst); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout& src, TensorLayout& dst); | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& dst); | |||
}; | |||
class GaussianBlurForward : public GaussianBlurBase { | |||
DEF_OPR_IMPL(GaussianBlurForward, GaussianBlurBase, 1, 1); | |||
public: | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout &src, TensorLayout &dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &dst) = 0; | |||
void deduce_layout(const TensorLayout& src, TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, const TensorLayout &dst, | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& dst, | |||
size_t workspace_in_bytes); | |||
}; | |||
using GaussianBlur = GaussianBlurForward; | |||
@@ -230,41 +232,75 @@ protected: | |||
size_t workspace_in_bytes); | |||
}; | |||
class SeparableFilterBase: public OperatorBase { | |||
/** | |||
* \brief Remap opr. | |||
*/ | |||
class RemapBase : public OperatorBase { | |||
DEF_OPR_PARAM(Remap); | |||
DEF_OPR_IMPL(RemapBase, OperatorBase, 2, 1); | |||
public: | |||
using InterpolationMode = Param::InterpolationMode; | |||
using BorderMode = Param::BorderMode; | |||
protected: | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& map_xy, | |||
const TensorLayout& dst); | |||
void deduce_layout_fwd(const TensorLayout& src, const TensorLayout& map_xy, | |||
TensorLayout& dst); | |||
}; | |||
class RemapForward : public RemapBase { | |||
DEF_OPR_IMPL(RemapForward, RemapBase, 2, 1); | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_in map_xy, | |||
_megdnn_tensor_out dst, _megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout& src, const TensorLayout& map_xy, | |||
TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& map_xy, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& map_xy, | |||
const TensorLayout& dst, size_t workspace_in_bytes); | |||
}; | |||
using Remap = RemapForward; | |||
class SeparableFilterBase : public OperatorBase { | |||
DEF_OPR_IMPL_CTOR(SeparableFilterBase, OperatorBase); | |||
DEF_OPR_PARAM(SeparableFilter); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout &src, | |||
const TensorLayout &filter_x, | |||
const TensorLayout &filter_y, | |||
TensorLayout &dst); | |||
void check_layout_fwd(const TensorLayout &src, | |||
const TensorLayout &filter_x, | |||
const TensorLayout &filter_y, | |||
const TensorLayout &dst); | |||
protected: | |||
void deduce_layout_fwd(const TensorLayout& src, | |||
const TensorLayout& filter_x, | |||
const TensorLayout& filter_y, TensorLayout& dst); | |||
void check_layout_fwd(const TensorLayout& src, const TensorLayout& filter_x, | |||
const TensorLayout& filter_y, | |||
const TensorLayout& dst); | |||
}; | |||
class SeparableFilterForward: public SeparableFilterBase { | |||
class SeparableFilterForward : public SeparableFilterBase { | |||
DEF_OPR_IMPL(SeparableFilterForward, SeparableFilterBase, 3, 1); | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, | |||
_megdnn_tensor_in filter_x, | |||
_megdnn_tensor_in filter_y, | |||
_megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout &src, | |||
const TensorLayout &filter_x, | |||
const TensorLayout &filter_y, | |||
TensorLayout &dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout &src, | |||
const TensorLayout &filter_x, | |||
const TensorLayout &filter_y, | |||
const TensorLayout &dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout &src, | |||
const TensorLayout &filter_x, | |||
const TensorLayout &filter_y, | |||
const TensorLayout &dst, size_t workspace_in_bytes); | |||
public: | |||
virtual void exec(_megdnn_tensor_in src, _megdnn_tensor_in filter_x, | |||
_megdnn_tensor_in filter_y, _megdnn_tensor_out dst, | |||
_megdnn_workspace workspace) = 0; | |||
void deduce_layout(const TensorLayout& src, const TensorLayout& filter_x, | |||
const TensorLayout& filter_y, TensorLayout& dst); | |||
virtual size_t get_workspace_in_bytes(const TensorLayout& src, | |||
const TensorLayout& filter_x, | |||
const TensorLayout& filter_y, | |||
const TensorLayout& dst) = 0; | |||
protected: | |||
void check_exec(const TensorLayout& src, const TensorLayout& filter_x, | |||
const TensorLayout& filter_y, const TensorLayout& dst, | |||
size_t workspace_in_bytes); | |||
}; | |||
using SeparableFilter = SeparableFilterForward; | |||
@@ -35,10 +35,10 @@ pdef('Axis').add_fields('int32', 'axis', 0) | |||
). | |||
add_enum(Doc('Format', 'convolution data/filter/output format; see ' | |||
':class:`RelayoutFormat` for more details'), | |||
'NCHW', 'NHWC', 'NHWCD4', 'NCHW4', 'NCHW8', 'NCHW32', 'NCHW88', 'NCHW44', | |||
Doc('NCHW_WINOGRAD', 'NCHW layout with weights tranformed by winograd'), | |||
Doc('NCHW88_WINOGRAD', 'NCHW88 layout with weights tranformed by winograd'), | |||
Doc('NCHW44_WINOGRAD', 'NCHW44 layout with weights tranformed by winograd'), | |||
'NCHW', 'NHWC', 'NHWCD4', 'NCHW4', 'NCHW8', 'NCHW32', 'NCHW88', 'NCHW44', | |||
Doc('NCHW_WINOGRAD', 'NCHW layout with weights tranformed by winograd'), | |||
Doc('NCHW88_WINOGRAD', 'NCHW88 layout with weights tranformed by winograd'), | |||
Doc('NCHW44_WINOGRAD', 'NCHW44 layout with weights tranformed by winograd'), | |||
Doc('CHWN4', 'CHWN4 is currently only used on Nvidia platform for fast implementation ' | |||
'of convolution using CUDA/SASS. The channels are splitted to groups of 4 channels.')) | |||
) | |||
@@ -699,6 +699,12 @@ pdef('UniformRNG').add_fields('uint64', 'seed', 0) | |||
.add_enum_alias('InterpolationMode', 'WarpPerspective', name_field='imode') | |||
.add_enum_alias('Format', 'ConvolutionV0', default=1)) | |||
(pdef('Remap', version=0) | |||
.add_enum_alias('InterpolationMode', 'WarpPerspective', name_field='imode') | |||
.add_enum_alias('BorderMode', 'WarpPerspective', name_field='border_type') | |||
.add_enum_alias('Format', 'ConvolutionV0', default=1) | |||
.add_fields('float32', 'scalar', '0.f')) | |||
(pdef('Convolution3D'). | |||
add_enum('Mode', 'CROSS_CORRELATION', 'CONVOLUTION'). | |||
add_fields( | |||
@@ -840,8 +846,8 @@ when the ``I`` suffix is present. | |||
'INTER_WEIGHT_CHAN', | |||
'INTER_WEIGHT_CHANI', | |||
'INTER_WEIGHT_DENSEI_DOT', | |||
'INTER_WEIGHT_GROUPI_DOT', | |||
'NCHW4_CHWN4', | |||
'INTER_WEIGHT_GROUPI_DOT', | |||
'NCHW4_CHWN4', | |||
'CHWN4_NCHW4', | |||
'NCHW_NCHW88_CONV_DENSE_WEIGHT', | |||
'NCHW_NCHW88_CONV_CHAN_WEIGHT', | |||
@@ -849,7 +855,7 @@ when the ``I`` suffix is present. | |||
'NCHW_NCHW88', | |||
'NCHW88_NCHW') | |||
) | |||
(pdef('SeparableFilter'). | |||
add_enum_alias('Format', 'ConvolutionV0'). | |||
@@ -882,10 +888,10 @@ when the ``I`` suffix is present. | |||
add_enum_alias('Format', 'ConvolutionV0'). | |||
add_fields('float32', 'spatial_scale', '1.0'). | |||
add_fields('float32', 'offset', '0.0'). | |||
add_fields('uint32', | |||
'pooled_height', '1', | |||
add_fields('uint32', | |||
'pooled_height', '1', | |||
'pooled_width', '1', | |||
'sample_height', '2', | |||
'sample_height', '2', | |||
'sample_width', '2') | |||
) | |||
(pdef('DeformablePSROIPooling'). | |||
@@ -188,6 +188,7 @@ private: | |||
cb(ROIAlignForward) \ | |||
cb(ROIAlignBackward) \ | |||
cb(BatchConvBiasForward) \ | |||
cb(Remap) \ | |||
/*! | |||
* \brief specialize HandleImpl::create_operator for a single opr type; | |||
@@ -0,0 +1,90 @@ | |||
/** | |||
* \file dnn/src/common/remap.cpp | |||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
* | |||
* Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#include "megdnn/oprs.h" | |||
#include "src/common/cv/common.h" | |||
#include "src/common/cv/helper.h" | |||
#include "src/common/utils.h" | |||
namespace megdnn { | |||
void RemapBase::deduce_layout_fwd(const TensorLayout& src, | |||
const TensorLayout& map_xy, | |||
TensorLayout& dst) { | |||
dst.dtype = src.dtype; | |||
dst.ndim = src.ndim; | |||
dst.shape[0] = src.shape[0]; | |||
size_t height_index, channel_index; | |||
if (param().format == param::Remap::Format::NHWC) { | |||
height_index = 1; | |||
channel_index = 3; | |||
} else { | |||
megdnn_assert(param().format == param::Remap::Format::NCHW); | |||
height_index = 2; | |||
channel_index = 1; | |||
} | |||
dst.shape[height_index] = map_xy.shape[1]; | |||
dst.shape[height_index + 1] = map_xy.shape[2]; | |||
dst.shape[channel_index] = src.shape[channel_index]; | |||
} | |||
void RemapBase::check_layout_fwd(const TensorLayout& src, | |||
const TensorLayout& map_xy, | |||
const TensorLayout& dst) { | |||
auto errmsg = [&]() { | |||
return megdnn_layout_msg(src) + ", " + megdnn_layout_msg(map_xy) + | |||
", " + megdnn_layout_msg(dst); | |||
}; | |||
MEGDNN_MARK_USED_VAR(errmsg); | |||
megdnn_assert(src.ndim == map_xy.ndim && src.ndim == dst.ndim && | |||
src.ndim == 4); | |||
megdnn_assert(dst.dtype == src.dtype); | |||
megdnn_assert(dst.shape[0] == src.shape[0], "%s", errmsg().c_str()); | |||
megdnn_assert(map_xy.shape[3] == 2); | |||
megdnn_assert(map_xy.shape[0] == src.shape[0]); | |||
// map_xy only support floa32 type | |||
// map_xy always in NHWC format | |||
megdnn_assert(map_xy.dtype.enumv() == DTypeEnum::Float32); | |||
// In remap opr, H, W is same as H W in map_xy. | |||
if (param().format == param::Remap::Format::NHWC) { | |||
megdnn_assert(src.shape[3] == dst.shape[3], "%s", errmsg().c_str()); | |||
megdnn_assert(dst.shape[2] == map_xy.shape[2] && | |||
dst.shape[1] == map_xy.shape[1], | |||
"%s", errmsg().c_str()); | |||
} else if (param().format == param::Remap::Format::NCHW) { | |||
megdnn_assert(src.shape[1] == dst.shape[1], "%s", errmsg().c_str()); | |||
megdnn_assert(dst.shape[2] == map_xy.shape[1] && | |||
dst.shape[3] == map_xy.shape[2], | |||
"%s", errmsg().c_str()); | |||
} else { | |||
megdnn_throw( | |||
"megdnn currently do not support other param.format except " | |||
"NHWC and NCHW"); | |||
} | |||
} | |||
void Remap::deduce_layout(const TensorLayout& src, const TensorLayout& map_xy, | |||
TensorLayout& dst) { | |||
deduce_layout_fwd(src, map_xy, dst); | |||
} | |||
void Remap::check_exec(const TensorLayout& src, const TensorLayout& map_xy, | |||
const TensorLayout& dst, size_t workspace_in_bytes) { | |||
check_layout_fwd(src, map_xy, dst); | |||
auto required_workspace_in_bytes = get_workspace_in_bytes(src, map_xy, dst); | |||
megdnn_assert(workspace_in_bytes >= required_workspace_in_bytes); | |||
} | |||
} // namespace megdnn | |||
// vim: syntax=cpp.doxygen |
@@ -74,6 +74,7 @@ | |||
#include "src/cuda/local_share/opr_impl.h" | |||
#include "src/cuda/roi_align/opr_impl.h" | |||
#include "src/cuda/batch_conv_bias/opr_impl.h" | |||
#include "src/cuda/remap/opr_impl.h" | |||
namespace megdnn { | |||
namespace cuda { | |||
@@ -0,0 +1,26 @@ | |||
/** | |||
* \file dnn/src/opencl/cuda/opr_impl.cpp | |||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
* | |||
* Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#include "src/cuda/remap/opr_impl.h" | |||
#include "megdnn/config/config.h" | |||
#include "src/common/utils.h" | |||
using namespace megdnn; | |||
using namespace cuda; | |||
void RemapImpl::exec(_megdnn_tensor_in src, _megdnn_tensor_out map_xy, | |||
_megdnn_tensor_in dst, _megdnn_workspace workspace) { | |||
check_exec(src.layout, map_xy.layout, dst.layout, workspace.size); | |||
megdnn_throw("megdnn currently do not support remap in cuda"); | |||
} | |||
// vim: syntax=cpp.doxygen |
@@ -0,0 +1,29 @@ | |||
/** | |||
* \file dnn/src/opencl/cuda/opr_impl.h | |||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
* | |||
* Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#pragma once | |||
#include "megdnn/oprs.h" | |||
namespace megdnn { | |||
namespace cuda { | |||
class RemapImpl final : public Remap { | |||
using Remap::Remap; | |||
void exec(_megdnn_tensor_in, _megdnn_tensor_in, _megdnn_tensor_out, | |||
_megdnn_workspace) override; | |||
size_t get_workspace_in_bytes(const TensorLayout&, const TensorLayout&, | |||
const TensorLayout&) override { | |||
return 0; | |||
} | |||
}; | |||
} // namespace cuda | |||
} // namespace megdnn | |||
// vim: syntax=cpp.doxygen |
@@ -75,6 +75,7 @@ | |||
#include "src/naive/warp_affine/opr_impl.h" | |||
#include "src/naive/warp_perspective/opr_impl.h" | |||
#include "src/naive/winograd_filter_preprocess/opr_impl.h" | |||
#include "src/naive/remap/opr_impl.h" | |||
static size_t g_image2d_pitch_alignment = 1; | |||
@@ -0,0 +1,194 @@ | |||
/** | |||
* \file dnn/src/naive/remap/opr_impl.cpp | |||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
* | |||
* Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#include "src/naive/remap/opr_impl.h" | |||
#include "src/common/cv/helper.h" | |||
#include "src/common/utils.h" | |||
#include "src/naive/handle.h" | |||
using namespace megdnn; | |||
using namespace naive; | |||
namespace { | |||
template <param::Remap::Format format> | |||
inline int get_offset(int, int, int, int, int, int); | |||
template <> | |||
inline int get_offset<param::Remap::Format::NCHW>(int height, int width, | |||
int channel, int h, int w, | |||
int) { | |||
return channel * h * w + height * w + width; | |||
} | |||
template <> | |||
inline int get_offset<param::Remap::Format::NHWC>(int height, int width, | |||
int channel, int, int w, | |||
int c) { | |||
return height * w * c + width * c + channel; | |||
} | |||
template <typename DataType, param::Remap::Format format, | |||
param::Remap::BorderMode bodertype> | |||
struct GetSrcData { | |||
static inline DataType get(const DataType*, int, int, int, int, int, int, | |||
int); | |||
}; | |||
template <typename DataType, param::Remap::Format format> | |||
struct GetSrcData<DataType, format, param::Remap::BorderMode::CONSTANT> { | |||
static inline DataType get(const DataType* src, int height, int width, | |||
int channel, int h, int w, int c, float scalar) { | |||
return (height >= 0 && height < h && width >= 0 && width < w) | |||
? src[get_offset<format>(height, width, channel, h, w, | |||
c)] | |||
: static_cast<DataType>(std::round(scalar)); | |||
} | |||
}; | |||
#define cb(bmode) \ | |||
template <typename DataType, param::Remap::Format format> \ | |||
struct GetSrcData<DataType, format, param::Remap::BorderMode::bmode> { \ | |||
static inline DataType get(const DataType* src, int height, int width, \ | |||
int channel, int h, int w, int c, float) { \ | |||
height = megcv::border_interpolate< \ | |||
param::Remap::BorderMode::bmode>(height, h); \ | |||
width = megcv::border_interpolate< \ | |||
param::Remap::BorderMode::bmode>(width, w); \ | |||
return src[get_offset<format>(height, width, channel, h, w, c)]; \ | |||
} \ | |||
}; | |||
cb(REPLICATE); | |||
cb(REFLECT); | |||
cb(REFLECT_101); | |||
cb(WRAP); | |||
#undef cb | |||
template <typename DataType, param::Remap::Format format, | |||
param::Remap::BorderMode bordertype> | |||
void remap_LINEAR(const DataType* src, const float* map_xy, DataType* dst, | |||
int N, int C, int IH, int IW, int OH, int OW, float scalar, | |||
std::function<DataType(float)> round) { | |||
for (int n = 0; n < N; | |||
++n, src += C * IH * IW, dst += C * OH * OW, map_xy += OH * OW * 2) { | |||
for (int h = 0; h < OH; ++h) { | |||
for (int w = 0; w < OW; ++w) { | |||
float index_col = map_xy[h * OW * 2 + w * 2 + 0]; | |||
float index_row = map_xy[h * OW * 2 + w * 2 + 1]; | |||
int col = static_cast<int>(floor(index_col)); | |||
int row = static_cast<int>(floor(index_row)); | |||
float v = index_col - col; | |||
float u = index_row - row; | |||
float one = 1.f; | |||
for (int c = 0; c < C; ++c) { | |||
DataType a00 = | |||
GetSrcData<DataType, format, bordertype>::get( | |||
src, row + 0, col + 0, c, IH, IW, C, | |||
scalar); | |||
DataType a01 = | |||
GetSrcData<DataType, format, bordertype>::get( | |||
src, row + 0, col + 1, c, IH, IW, C, | |||
scalar); | |||
DataType a10 = | |||
GetSrcData<DataType, format, bordertype>::get( | |||
src, row + 1, col + 0, c, IH, IW, C, | |||
scalar); | |||
DataType a11 = | |||
GetSrcData<DataType, format, bordertype>::get( | |||
src, row + 1, col + 1, c, IH, IW, C, | |||
scalar); | |||
dst[get_offset<format>(h, w, c, OH, OW, C)] = | |||
static_cast<DataType>( | |||
round(a00 * (one - u) * (one - v) + | |||
a01 * (one - u) * v + | |||
a10 * (one - v) * u + a11 * u * v)); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
template <typename DataType, DTypeCategory cat> | |||
struct Round { | |||
static inline DataType round(float x) { return std::round(x); } | |||
}; | |||
template <typename DataType> | |||
struct Round<DataType, DTypeCategory::FLOAT> { | |||
static inline DataType round(float x) { return static_cast<DataType>(x); } | |||
}; | |||
} // namespace | |||
void RemapImpl::exec(_megdnn_tensor_in src, _megdnn_tensor_in map_xy, | |||
_megdnn_tensor_out dst, _megdnn_workspace workspace) { | |||
check_exec(src.layout, map_xy.layout, dst.layout, workspace.size); | |||
int N, C, IH, IW, OH, OW; | |||
if (param().format == param::Remap::Format::NCHW) { | |||
N = src.layout.shape[0]; | |||
C = src.layout.shape[1]; | |||
IH = src.layout.shape[2]; | |||
IW = src.layout.shape[3]; | |||
} else { | |||
N = src.layout.shape[0]; | |||
C = src.layout.shape[3]; | |||
IH = src.layout.shape[1]; | |||
IW = src.layout.shape[2]; | |||
} | |||
OH = map_xy.layout.shape[1]; | |||
OW = map_xy.layout.shape[2]; | |||
switch (src.layout.dtype.enumv()) { | |||
#define cb(dt, fmt, border, interpolation) \ | |||
if (param().format == param::Remap::Format::fmt && \ | |||
param().border_type == param::Remap::BorderMode::border && \ | |||
param().imode == param::Remap::InterpolationMode::interpolation) { \ | |||
using ctype = DTypeTrait<dt>::ctype; \ | |||
MEGDNN_DISPATCH_CPU_KERN_OPR( \ | |||
(remap_##interpolation<ctype, param::Remap::Format::fmt, \ | |||
param::Remap::BorderMode::border>( \ | |||
src.compatible_ptr<ctype>(), \ | |||
map_xy.compatible_ptr<dt_float32>(), \ | |||
dst.compatible_ptr<ctype>(), N, C, IH, IW, OH, OW, \ | |||
param().scalar, \ | |||
Round<ctype, DTypeTrait<dt>::category>::round))); \ | |||
break; \ | |||
} | |||
#define support_dtype(dt) \ | |||
case DTypeTrait<dt>::enumv: { \ | |||
cb(dt, NCHW, CONSTANT, LINEAR); \ | |||
cb(dt, NCHW, REPLICATE, LINEAR); \ | |||
cb(dt, NCHW, REFLECT, LINEAR); \ | |||
cb(dt, NCHW, REFLECT_101, LINEAR); \ | |||
cb(dt, NCHW, WRAP, LINEAR); \ | |||
cb(dt, NHWC, CONSTANT, LINEAR); \ | |||
cb(dt, NHWC, REPLICATE, LINEAR); \ | |||
cb(dt, NHWC, REFLECT, LINEAR); \ | |||
cb(dt, NHWC, REFLECT_101, LINEAR); \ | |||
cb(dt, NHWC, WRAP, LINEAR); \ | |||
megdnn_throw( \ | |||
"format, border type or imode is incorrect in remap navie " \ | |||
"with dtype = " #dt); \ | |||
} | |||
support_dtype(dtype::Float32); | |||
MEGDNN_INC_FLOAT16(support_dtype(dtype::Float16)); | |||
support_dtype(dtype::Int8); | |||
support_dtype(dtype::Uint8); | |||
#undef cb | |||
#undef support_dtype | |||
default: | |||
megdnn_throw("unsupported dtype in remap naive\n"); | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
/** | |||
* \file dnn/src/naive/remap/opr_impl.h | |||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
* | |||
* Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#pragma once | |||
#include "megdnn/oprs.h" | |||
namespace megdnn { | |||
namespace naive { | |||
class RemapImpl final : public Remap { | |||
using Remap::Remap; | |||
void exec(_megdnn_tensor_in, _megdnn_tensor_in, _megdnn_tensor_out, | |||
_megdnn_workspace) override; | |||
size_t get_workspace_in_bytes(const TensorLayout&, const TensorLayout&, | |||
const TensorLayout&) override { | |||
return 0; | |||
} | |||
}; | |||
} // namespace naive | |||
} // namespace megdnn | |||
// vim: syntax=cpp.doxygen |
@@ -105,6 +105,7 @@ DEF(DeformableConvBackwardData, 8, true, false); | |||
DEF(DeformablePSROIPoolingForward, 5, true, true); | |||
DEF(DeformablePSROIPoolingBackward, 7, true, false); | |||
DEF(BatchConvBiasForward, 5, true, true); | |||
DEF(Remap, 3, true, true); | |||
} // namespace test | |||
} // namespace megdnn | |||
@@ -0,0 +1,127 @@ | |||
/** | |||
* \file dnn/test/common/remap.h | |||
* MegEngine is Licensed under the Apache License, Version 2.0 (the "License") | |||
* | |||
* Copyright (c) 2014-2020 Megvii Inc. All rights reserved. | |||
* | |||
* Unless required by applicable law or agreed to in writing, | |||
* software distributed under the License is distributed on an | |||
* "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
* implied. | |||
*/ | |||
#pragma once | |||
#include <iostream> | |||
#include "megdnn/basic_types.h" | |||
#include "megdnn/opr_param_defs.h" | |||
#include "./rng.h" | |||
namespace megdnn { | |||
namespace test { | |||
namespace remap { | |||
struct TestArg { | |||
param::Remap param; | |||
TensorShape src; | |||
TensorShape map_xy; | |||
TensorShape dst; | |||
TestArg(param::Remap param_, TensorShape src_, TensorShape map_xy_, | |||
TensorShape dst_) | |||
: param(param_), src(src_), map_xy(map_xy_), dst(dst_) {} | |||
}; | |||
static inline std::vector<TestArg> get_nchw_args() { | |||
std::vector<TestArg> args; | |||
param::Remap param; | |||
std::vector<param::Remap::Format> format_vec = {param::Remap::Format::NCHW}; | |||
std::vector<param::Remap::BorderMode> border_mode_vec = { | |||
param::Remap::BorderMode::CONSTANT, | |||
param::Remap::BorderMode::REFLECT_101, | |||
param::Remap::BorderMode::REFLECT, | |||
param::Remap::BorderMode::WRAP, | |||
param::Remap::BorderMode::REPLICATE}; | |||
// current do not test this. | |||
std::vector<float> scalar; | |||
for (auto fmt : format_vec) { | |||
for (auto border_type : border_mode_vec) { | |||
param.format = fmt; | |||
param.border_type = border_type; | |||
args.emplace_back(param, TensorShape{1, 1, 2, 2}, | |||
TensorShape{1, 2, 2, 2}, TensorShape{1, 1, 2, 2}); | |||
args.emplace_back(param, TensorShape{1, 3, 2, 2}, | |||
TensorShape{1, 2, 2, 2}, TensorShape{1, 3, 2, 2}); | |||
args.emplace_back(param, TensorShape{1, 10, 100, 100}, | |||
TensorShape{1, 100, 100, 2}, | |||
TensorShape{1, 10, 100, 100}); | |||
args.emplace_back(param, TensorShape{2, 4, 100, 200}, | |||
TensorShape{2, 100, 200, 2}, | |||
TensorShape{2, 4, 100, 200}); | |||
args.emplace_back(param, TensorShape{2, 4, 100, 200}, | |||
TensorShape{2, 20, 30, 2}, | |||
TensorShape{2, 4, 20, 30}); | |||
args.emplace_back(param, TensorShape{2, 4, 10, 10}, | |||
TensorShape{2, 20, 30, 2}, | |||
TensorShape{2, 4, 20, 30}); | |||
} | |||
} | |||
return args; | |||
} | |||
static inline std::vector<TestArg> get_nhwc_args() { | |||
std::vector<TestArg> args; | |||
param::Remap param; | |||
std::vector<param::Remap::Format> format_vec = {param::Remap::Format::NHWC}; | |||
std::vector<param::Remap::BorderMode> border_mode_vec = { | |||
param::Remap::BorderMode::CONSTANT, | |||
param::Remap::BorderMode::REFLECT_101, | |||
param::Remap::BorderMode::REFLECT, | |||
param::Remap::BorderMode::WRAP, | |||
param::Remap::BorderMode::REPLICATE}; | |||
// current do not test this. | |||
std::vector<float> scalar; | |||
for (auto fmt : format_vec) { | |||
for (auto border_type : border_mode_vec) { | |||
param.format = fmt; | |||
param.border_type = border_type; | |||
param.scalar = 12.f; | |||
args.emplace_back(param, TensorShape{1, 2, 2, 1}, | |||
TensorShape{1, 2, 2, 2}, TensorShape{1, 2, 2, 1}); | |||
args.emplace_back(param, TensorShape{1, 2, 2, 3}, | |||
TensorShape{1, 2, 2, 2}, TensorShape{1, 2, 2, 3}); | |||
args.emplace_back(param, TensorShape{1, 2, 2, 66}, | |||
TensorShape{1, 2, 2, 2}, | |||
TensorShape{1, 2, 2, 66}); | |||
args.emplace_back(param, TensorShape{1, 100, 100, 10}, | |||
TensorShape{1, 100, 100, 2}, | |||
TensorShape{1, 100, 100, 10}); | |||
args.emplace_back(param, TensorShape{2, 100, 200, 4}, | |||
TensorShape{2, 100, 200, 2}, | |||
TensorShape{2, 100, 200, 4}); | |||
args.emplace_back(param, TensorShape{2, 100, 200, 4}, | |||
TensorShape{2, 20, 30, 2}, | |||
TensorShape{2, 20, 30, 4}); | |||
args.emplace_back(param, TensorShape{2, 10, 10, 4}, | |||
TensorShape{2, 20, 30, 2}, | |||
TensorShape{2, 20, 30, 4}); | |||
} | |||
} | |||
return args; | |||
} | |||
} // namespace remap | |||
} // namespace test | |||
} // namespace megdnn | |||
// vim: syntax=cpp.doxygen |
@@ -842,6 +842,30 @@ std::unique_ptr<ConvertF32ToF16Pass> ConvertF32ToF16Pass::make( | |||
return new_warp.node()->owner_opr(); | |||
}; | |||
auto replace_remap_opr = [](OperatorNodeBase* opr, | |||
const VarNodeArray& new_inp) { | |||
mgb_assert(opr->input().size() == new_inp.size() && | |||
(new_inp.size() == 2)); | |||
auto& remap_opr = opr->cast_final<opr::Remap>(); | |||
// map tensor must be float32 | |||
auto new_map = new_inp[1]; | |||
if (new_inp[1]->dtype() != dtype::Float32()) { | |||
if (try_cast_as_op<opr::TypeCvt>(new_map->owner_opr()) && | |||
new_map->owner_opr()->input(0)->dtype() == dtype::Float32()) | |||
new_map = new_map->owner_opr()->input(0); | |||
else | |||
new_map = | |||
opr::TypeCvt::make(new_inp[1], dtype::Float32(), {}).node(); | |||
} | |||
SymbolVar new_remap; | |||
new_remap = opr::Remap::make(new_inp[0], new_map, | |||
remap_opr.param(), | |||
remap_opr.config()); | |||
return new_remap.node()->owner_opr(); | |||
}; | |||
auto ret = std::make_unique<ConvertF32ToF16Pass>(); | |||
// don't check dtype | |||
ret->set_var_replace_check_flag(VarReplaceCheckFlag::CHECK_ALL ^ | |||
@@ -855,6 +879,7 @@ std::unique_ptr<ConvertF32ToF16Pass> ConvertF32ToF16Pass::make( | |||
replace_func[opr::ImmutableTensor::typeinfo()] = replace_imt_opr; | |||
replace_func[opr::TypeCvt::typeinfo()] = replace_cvt_opr; | |||
replace_func[opr::WarpPerspective::typeinfo()] = replace_warp_opr; | |||
replace_func[opr::Remap::typeinfo()] = replace_remap_opr; | |||
return ret; | |||
#endif | |||
} | |||
@@ -693,6 +693,46 @@ TEST(TestGoptInference, Float16IOFloat32ComputeWarpPerspective) { | |||
MGB_ASSERT_TENSOR_NEAR(host_y, host_y_opt, 1e-3); | |||
} | |||
TEST(TestGoptInference, Float16IOFloat32ComputeRemap) { | |||
auto cn = CompNode::load("cpu1"); | |||
constexpr size_t INP_H = 10, INP_W = 10, N = 2; | |||
HostTensorGenerator<> gen; | |||
auto graph = ComputingGraph::make(); | |||
auto mkvar = [&](const char* name, const TensorShape& shp) { | |||
return opr::Host2DeviceCopy::make(*graph, gen(shp, cn)).rename(name); | |||
}; | |||
graph->options().graph_opt_level = 0; | |||
auto a = mkvar("a", {N, 4, INP_H, INP_W}); | |||
auto gen_map = [&](HostTensorND& mat) { | |||
auto ptr = mat.ptr<float>(); | |||
for(size_t n = 0; n < N; ++n){ | |||
for(int h = 0; h < 5; ++h){ | |||
for(int w = 0; w < 5; ++w){ | |||
*ptr++ = (h * 5 * 2) + 5 * 2 + 0; | |||
*ptr++ = (h * 5 * 2) + 5 * 2 + 1; | |||
} | |||
} | |||
} | |||
mgb_assert(ptr == mat.ptr<float>() + mat.shape().total_nr_elems()); | |||
}; | |||
auto map_host = std::make_shared<HostTensorND>( | |||
a.node()->comp_node(), TensorShape{N, 5, 5, 2}, dtype::Float32()); | |||
gen_map(*map_host); | |||
auto map = opr::Host2DeviceCopy::make(*graph, map_host).rename("map"); | |||
auto y = opr::Remap::make(a, map); | |||
SymbolVar y_opt; | |||
unpack_vector(gopt::optimize_for_inference( | |||
{y}, gopt::OptimizeForInferenceOptions{} | |||
.enable_f16_io_f32_comp()), | |||
y_opt); | |||
ASSERT_EQ(y_opt.dtype(), dtype::Float32()); | |||
HostTensorND host_y, host_y_opt; | |||
auto func = graph->compile({make_callback_copy(y, host_y), | |||
make_callback_copy(y_opt, host_y_opt)}); | |||
func->execute(); | |||
MGB_ASSERT_TENSOR_NEAR(host_y, host_y_opt, 1e-3); | |||
} | |||
TEST(TestGoptInference, Uint8IOFloat16ComputeWarpPerspective) { | |||
constexpr size_t INP_H = 10, INP_W = 10, N = 2; | |||
HostTensorGenerator<dtype::Uint8> gen_uint8; | |||
@@ -1987,7 +2027,7 @@ TEST(TestGoptInference, EnableCHWN4WarpPespective) { | |||
auto y = opr::ConvBiasForward::make( | |||
x, w, b, param, {}, OperatorNodeConfig{dtype::QuantizedS8{2.5f}}); | |||
opr::WarpPerspective::Param warp_param; | |||
warp_param.format = opr::WarpPerspective::Param::Format::NCHW4; | |||
auto y1 = opr::WarpPerspective::make(y, mat_var, TensorShape{16, 16}, warp_param); | |||
@@ -316,4 +316,13 @@ void WarpAffineForward::record_execute_deps(ExecDependencyArray &deps) { | |||
record_megdnn_opr(deps); | |||
} | |||
/* ======================= RemapForward ======================= */ | |||
MGB_DYN_TYPE_OBJ_FINAL_IMPL(RemapForward); | |||
MEGDNN_OPR_INIT2(RemapForward, "remap") | |||
void RemapForward::init_output_dtype(){ | |||
output(0)->dtype(input(0)->dtype()); | |||
} | |||
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} |
@@ -79,4 +79,15 @@ decl_opr( | |||
'for details on affine transformations.', | |||
version=1) | |||
decl_opr( | |||
'Remap', | |||
inputs=[ | |||
Doc('src', 'input image, in NCHW format or NHWC format'), | |||
Doc('map_xy', 'map matrix with NHWC format. C must euqal to 2. ' | |||
'dst(x, y) = src(mapX(x, y), mapY(x, y)' | |||
'col in channel 0, and row in channel 1')], | |||
params='Remap', | |||
desc='Remap transformation to batched 2D images; ' | |||
'see https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html?highlight=remap' | |||
'for details on remap transformations.') | |||
# vim: ft=python |
@@ -50,6 +50,7 @@ namespace opr { | |||
MGB_SEREG_OPR(GaussianBlur, 1); | |||
MGB_SEREG_OPR(ResizeBackward, 2); | |||
MGB_SEREG_OPR(Remap, 2); | |||
//! current warp affine version | |||
using WarpAffineV1 = opr::WarpAffine; | |||
@@ -165,6 +165,21 @@ MGB_DEFINE_OPR_CLASS(ResizeBackward, | |||
const OperatorNodeConfig &config = {}); | |||
}; | |||
MGB_DEFINE_OPR_CLASS(RemapForward, | |||
intl::MegDNNOprWrapperFwd<megdnn::RemapForward>) // { | |||
public: | |||
RemapForward( | |||
VarNode *in_tensor, VarNode* map, | |||
const Param ¶m, const OperatorNodeConfig &config); | |||
static SymbolVar make(SymbolVar in_tensor, SymbolVar map, const Param ¶m = {}, | |||
const OperatorNodeConfig &config = {}); | |||
private: | |||
void init_output_dtype() override; | |||
}; | |||
using Remap = RemapForward; | |||
/*! | |||
* \brief apply affine transformation to batched 2D images | |||
* | |||
@@ -636,4 +636,55 @@ TEST(TestOprImgproc, WarpAffineForward) { | |||
run({TensorShape{N, 10, 9, C}, {N, 2, 3}}, opt); | |||
} | |||
TEST(TestOprImgproc, Remap_NCHW) { | |||
constexpr size_t N = 2, C = 8; | |||
opr::Remap::Param param; | |||
using Checker = AutoOprChecker<2, 1>; | |||
TensorShape out_shp{N, C, 10, 10}; | |||
param.format = opr::Remap::Param::Format::NCHW; | |||
auto make_graph = [&](const Checker::SymInpArray &inputs) -> | |||
Checker::SymOutArray { | |||
return {opr::Remap::make(inputs[0], inputs[1], param)}; | |||
}; | |||
auto fwd = [&](Checker::NumOutArray &dest, Checker::NumInpArray inp) { | |||
auto opr = megdnn_naive_handle()->create_operator<megdnn::Remap>(); | |||
opr->param() = param; | |||
dest[0].resize(out_shp); | |||
opr->exec(inp[0]->as_megdnn(), inp[1]->as_megdnn(), dest[0].as_megdnn(), {}); | |||
}; | |||
Checker::RunOptions opt; | |||
Checker(make_graph, fwd, CompNode::load("cpu1")) | |||
.disable_grad_check() | |||
.run({TensorShape{N, C, 3, 20}, TensorShape{N, 10, 10, 2}}, opt) | |||
.run({TensorShape{N, C, 6, 5}, TensorShape{N, 10, 10, 2}}, opt) | |||
.run({TensorShape{N, C, 20, 20}, TensorShape{N, 10, 10, 2}}, opt); | |||
} | |||
TEST(TestOprImgproc, Remap_NHWC) { | |||
constexpr size_t N = 2, C = 8; | |||
opr::Remap::Param param; | |||
using Checker = AutoOprChecker<2, 1>; | |||
TensorShape out_shp{N, 10, 10, C}; | |||
param.format = opr::Remap::Param::Format::NHWC; | |||
auto make_graph = [&](const Checker::SymInpArray &inputs) -> | |||
Checker::SymOutArray { | |||
return {opr::Remap::make(inputs[0], inputs[1], param)}; | |||
}; | |||
auto fwd = [&](Checker::NumOutArray &dest, Checker::NumInpArray inp) { | |||
auto opr = megdnn_naive_handle()->create_operator<megdnn::Remap>(); | |||
opr->param() = param; | |||
dest[0].resize(out_shp); | |||
opr->exec(inp[0]->as_megdnn(), inp[1]->as_megdnn(), dest[0].as_megdnn(), {}); | |||
}; | |||
Checker::RunOptions opt; | |||
Checker(make_graph, fwd, CompNode::load("cpu1")) | |||
.disable_grad_check() | |||
.run({TensorShape{N, 3, 20, C}, TensorShape{N, 10, 10, 2}}, opt) | |||
.run({TensorShape{N, 6, 5, C}, TensorShape{N, 10, 10, 2}}, opt) | |||
.run({TensorShape{N, 20, 20, C}, TensorShape{N, 10, 10, 2}}, opt); | |||
} | |||
// vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}} |
@@ -70,6 +70,7 @@ union OperatorParam { | |||
param.WarpAffine, | |||
param.GaussianBlur, | |||
param.Resize, | |||
param.Remap, | |||
param.Convolution3D, | |||
param.Conv3DBias, | |||
param.SeparableConv3D, | |||