提交 1255c9f1 编写于 作者: M Megvii Engine Team 提交者: Xinran Xu

feat(mge/opr): add opr remap in opencl and naive

GitOrigin-RevId: 454078866002616900789f86f0ab80786b9dc160
上级 03d68058
......@@ -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;
......
/**
* \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 {
......
/**
* \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
/**
* \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;
......
/**
* \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");
}
}
/**
* \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
......
/**
* \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 &param, const OperatorNodeConfig &config);
static SymbolVar make(SymbolVar in_tensor, SymbolVar map, const Param &param = {},
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,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册