You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by zh...@apache.org on 2021/01/15 21:22:58 UTC
[incubator-mxnet] branch v1.x updated: [v1.x] ONNX support for
box_decode (#19750)
This is an automated email from the ASF dual-hosted git repository.
zha0q1 pushed a commit to branch v1.x
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git
The following commit(s) were added to refs/heads/v1.x by this push:
new d6abe00 [v1.x] ONNX support for box_decode (#19750)
d6abe00 is described below
commit d6abe0068ce3106d9afbc9d6c75426f158749ace
Author: waytrue17 <52...@users.noreply.github.com>
AuthorDate: Fri Jan 15 13:20:38 2021 -0800
[v1.x] ONNX support for box_decode (#19750)
* box_decode
* fix sanity
Co-authored-by: Wei Chu <we...@amazon.com>
---
.../mxnet/contrib/onnx/mx2onnx/_op_translations.py | 77 ++++++++++++++++++++++
tests/python-pytest/onnx/test_operators.py | 13 ++++
2 files changed, 90 insertions(+)
diff --git a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py
index ecfb032..1c50abe 100644
--- a/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py
+++ b/python/mxnet/contrib/onnx/mx2onnx/_op_translations.py
@@ -2946,3 +2946,80 @@ def convert_where(node, **kwargs):
make_node("Where", [name+"_bool", input_nodes[1], input_nodes[2]], [name], name=name)
]
return nodes
+
+@mx_op.register("_contrib_box_decode")
+def convert_contrib_box_decode(node, **kwargs):
+ """Map MXNet's _contrib_box_decode operator attributes to onnx's operator.
+ """
+ from onnx.helper import make_node
+ name, input_nodes, attrs = get_inputs(node, kwargs)
+
+ data = input_nodes[0]
+ anchors = input_nodes[1]
+ input_type = kwargs['in_type']
+ fmt = attrs.get('format', 'center')
+ std0 = float(attrs.get('std0', '1.'))
+ std1 = float(attrs.get('std1', '1.'))
+ std2 = float(attrs.get('std2', '1.'))
+ std3 = float(attrs.get('std3', '1.'))
+ clip = float(attrs.get('clip', '-1.'))
+
+ if fmt not in ['center', 'corner']:
+ raise NotImplementedError("format must be either corner or center.")
+
+ nodes = [
+ create_tensor([0], name+'_0', kwargs["initializer"]),
+ create_tensor([2], name+'_2', kwargs["initializer"]),
+ create_tensor([4], name+'_4', kwargs["initializer"]),
+ create_tensor([2], name+'_2f', kwargs["initializer"], dtype='float32'),
+ create_tensor([clip], name+'_clip', kwargs["initializer"], dtype='float32'),
+ create_tensor([std0, std1, std2, std3], name+'_std_1d', kwargs["initializer"], dtype='float32'),
+ create_tensor([1, 4], name+'_std_shape', kwargs["initializer"]),
+ make_node("Cast", [data], [name+'_data'], to=int(onnx.TensorProto.FLOAT)),
+ make_node("Cast", [anchors], [name+'_anchors'], to=int(onnx.TensorProto.FLOAT)),
+ make_node('Reshape', [name+'_std_1d', name+'_std_shape'], [name+'_std']),
+ make_node("Mul", [name+'_data', name+'_std'], [name+'_mul0_out']),
+ make_node('Slice', [name+'_mul0_out', name+'_0', name+'_2', name+'_2'], [name+'_data_xy']),
+ make_node('Slice', [name+'_mul0_out', name+'_2', name+'_4', name+'_2'], [name+'_data_wh']),
+ ]
+
+ if fmt == 'corner':
+ nodes += [
+ make_node('Slice', [name+'_anchors', name+'_0', name+'_2', name+'_2'], [name+'_slice0_out']),
+ make_node('Slice', [name+'_anchors', name+'_2', name+'_4', name+'_2'], [name+'_slice1_out']),
+ make_node('Sub', [name+'_slice1_out', name+'_slice0_out'], [name+'_anchor_wh']),
+ make_node('Div', [name+'_anchor_wh', name+'_2f'], [name+'_div0_out']),
+ make_node("Add", [name+'_slice0_out', name+'_div0_out'], [name+'_anchor_xy']),
+ ]
+ else:
+ nodes += [
+ make_node('Slice', [name+'_anchors', name+'_0', name+'_2', name+'_2'], [name+'_anchor_xy']),
+ make_node('Slice', [name+'_anchors', name+'_2', name+'_4', name+'_2'], [name+'_anchor_wh']),
+ ]
+
+ nodes += [
+ make_node("Mul", [name+'_data_xy', name+'_anchor_wh'], [name+'_mul1_out']),
+ make_node("Add", [name+'_mul1_out', name+'_anchor_xy'], [name+'_add0_out']),
+ ]
+
+ if clip > 0.:
+ nodes += [
+ make_node("Less", [name+"_data_wh", name+"_clip"], [name+"_less0_out"]),
+ make_node('Where', [name+'_less0_out', name+'_data_wh', name+'_clip'], [name+'_where0_out']),
+ make_node("Exp", [name+'_where0_out'], [name+'_exp0_out']),
+ ]
+ else:
+ nodes += [
+ make_node("Exp", [name+'_data_wh'], [name+'_exp0_out']),
+ ]
+
+ nodes += [
+ make_node("Mul", [name+'_exp0_out', name+'_anchor_wh'], [name+'_mul2_out']),
+ make_node('Div', [name+'_mul2_out', name+'_2f'], [name+'_div1_out']),
+ make_node('Sub', [name+'_add0_out', name+'_div1_out'], [name+'_sub0_out']),
+ make_node('Add', [name+'_add0_out', name+'_div1_out'], [name+'_add1_out']),
+ make_node('Concat', [name+'_sub0_out', name+'_add1_out'], [name+'concat0_out'], axis=2),
+ make_node("Cast", [name+'concat0_out'], [name], to=input_type, name=name)
+ ]
+
+ return nodes
diff --git a/tests/python-pytest/onnx/test_operators.py b/tests/python-pytest/onnx/test_operators.py
index f9a6abc..4882321 100644
--- a/tests/python-pytest/onnx/test_operators.py
+++ b/tests/python-pytest/onnx/test_operators.py
@@ -408,3 +408,16 @@ def test_onnx_export_where(tmp_path, dtype, shape):
y = mx.nd.ones(shape, dtype=dtype)
cond = mx.nd.random.randint(low=0, high=1, shape=shape, dtype='int32')
op_export_test('where', M, [cond, x, y], tmp_path)
+
+@pytest.mark.parametrize('dtype', ['float16', 'float32'])
+@pytest.mark.parametrize('fmt', ['corner', 'center'])
+@pytest.mark.parametrize('clip', [-1., 0., .5, 5.])
+def test_onnx_export_contrib_box_decode(tmp_path, dtype, fmt, clip):
+ # ensure data[0] < data[2] and data[1] < data[3] for corner format
+ mul = mx.nd.array([-1, -1, 1, 1], dtype=dtype)
+ data = mx.nd.random.uniform(0, 1, (2, 3, 4), dtype=dtype) * mul
+ anchors = mx.nd.random.uniform(0, 1, (1, 3, 4), dtype=dtype) * mul
+ M1 = def_model('contrib.box_decode', format=fmt, clip=clip)
+ op_export_test('contrib_box_decode', M1, [data, anchors], tmp_path)
+ M2 = def_model('contrib.box_decode', format=fmt, clip=clip, std0=0.3, std1=1.4, std2=0.5, std3=1.6)
+ op_export_test('contrib_box_decode', M1, [data, anchors], tmp_path)