From 96539d2a3e8ec8d6f87312485733d4279c282dab Mon Sep 17 00:00:00 2001 From: Matthew Brookhart Date: Mon, 8 Nov 2021 14:07:19 -0700 Subject: [PATCH 1/6] add fallback to opset 11 --- python/tvm/relay/frontend/onnx.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/tvm/relay/frontend/onnx.py b/python/tvm/relay/frontend/onnx.py index 8eda1c95fde7..25da10690ecb 100644 --- a/python/tvm/relay/frontend/onnx.py +++ b/python/tvm/relay/frontend/onnx.py @@ -2639,6 +2639,12 @@ def _impl_v11(cls, inputs, attr, params): def _impl_v13(cls, inputs, attr, params): scale = inputs[2] size = inputs[3] + + # Some versions of onnx exporters produce an opset 13 model with the opset 11 + # resize op, handle that edge case + if scale is not None and size is not None: + return cls._impl_v11(inputs, attr, params) + if size is not None: assert scale is None, "One of scale or size should be passed, not both." else: From 4c8121a040d0d0c15d4b3e9ccb40218856625038 Mon Sep 17 00:00:00 2001 From: Matthew Brookhart Date: Mon, 8 Nov 2021 22:12:30 -0700 Subject: [PATCH 2/6] Support tf_crop_and_resize in resize op --- include/tvm/relay/attrs/image.h | 24 ++++ python/tvm/relay/frontend/onnx.py | 45 ++++++-- python/tvm/relay/op/dyn/image/_image.py | 6 + python/tvm/relay/op/image/_image.py | 12 ++ python/tvm/relay/op/image/image.py | 70 ++++++++++-- python/tvm/topi/image/resize.py | 125 +++++++++++++++++---- python/tvm/topi/nn/upsampling.py | 2 + src/relay/op/dyn/image/resize.cc | 16 +-- src/relay/op/image/resize.cc | 33 ++++-- src/relay/op/make_op.h | 7 +- src/relay/transforms/dynamic_to_static.cc | 26 +++-- src/relay/transforms/pattern_utils.h | 17 +++ tests/python/frontend/onnx/test_forward.py | 2 +- 13 files changed, 319 insertions(+), 66 deletions(-) diff --git a/include/tvm/relay/attrs/image.h b/include/tvm/relay/attrs/image.h index b851add61e4a..78687b38ee76 100644 --- a/include/tvm/relay/attrs/image.h +++ b/include/tvm/relay/attrs/image.h @@ -35,16 +35,21 @@ namespace relay { /*! \brief Attributes used in image resize1d operator */ struct Resize1DAttrs : public tvm::AttrsNode { Array size; + Array roi; std::string layout; std::string method; std::string coordinate_transformation_mode; std::string rounding_method; double cubic_alpha; int cubic_exclude; + double extrapolation_value; DataType out_dtype; TVM_DECLARE_ATTRS(Resize1DAttrs, "relay.attrs.Resize1DAttrs") { TVM_ATTR_FIELD(size).set_default(NullValue >()).describe("Output Size."); + TVM_ATTR_FIELD(roi) + .set_default(NullValue >()) + .describe("Region of Interest for coordinate transformation mode 'tf_crop_and_resize'"); TVM_ATTR_FIELD(layout).set_default("NCW").describe( "Dimension ordering of input data. Can be 'NCW', 'NWC', etc." "'N', 'C', 'W' stands for batch, channel and width" @@ -73,6 +78,9 @@ struct Resize1DAttrs : public tvm::AttrsNode { TVM_ATTR_FIELD(cubic_exclude) .set_default(0) .describe("Flag to exclude exterior of the image during cubic interpolation"); + TVM_ATTR_FIELD(extrapolation_value) + .set_default(0.0) + .describe("Value to return when roi is outside of the image"); TVM_ATTR_FIELD(out_dtype).set_default(NullValue()).describe("Output data type."); } }; @@ -80,16 +88,21 @@ struct Resize1DAttrs : public tvm::AttrsNode { /*! \brief Attributes used in image resize2d operator */ struct Resize2DAttrs : public tvm::AttrsNode { Array size; + Array roi; std::string layout; std::string method; std::string coordinate_transformation_mode; std::string rounding_method; double cubic_alpha; int cubic_exclude; + double extrapolation_value; DataType out_dtype; TVM_DECLARE_ATTRS(Resize2DAttrs, "relay.attrs.Resize2DAttrs") { TVM_ATTR_FIELD(size).set_default(NullValue >()).describe("Output Size."); + TVM_ATTR_FIELD(roi) + .set_default(NullValue >()) + .describe("Region of Interest for coordinate transformation mode 'tf_crop_and_resize'"); TVM_ATTR_FIELD(layout).set_default("NCHW").describe( "Dimension ordering of input data. Can be 'NCHW', 'NHWC', etc." "'N', 'C', 'H', 'W' stands for batch, channel, height, and width" @@ -118,6 +131,9 @@ struct Resize2DAttrs : public tvm::AttrsNode { TVM_ATTR_FIELD(cubic_exclude) .set_default(0) .describe("Flag to exclude exterior of the image during bicubic interpolation"); + TVM_ATTR_FIELD(extrapolation_value) + .set_default(0.0) + .describe("Value to return when roi is outside of the image"); TVM_ATTR_FIELD(out_dtype).set_default(NullValue()).describe("Output data type."); } }; @@ -125,16 +141,21 @@ struct Resize2DAttrs : public tvm::AttrsNode { /*! \brief Attributes used in image resize3d operator */ struct Resize3DAttrs : public tvm::AttrsNode { Array size; + Array roi; std::string layout; std::string method; std::string coordinate_transformation_mode; std::string rounding_method; double cubic_alpha; int cubic_exclude; + double extrapolation_value; DataType out_dtype; TVM_DECLARE_ATTRS(Resize3DAttrs, "relay.attrs.Resize3DAttrs") { TVM_ATTR_FIELD(size).set_default(NullValue >()).describe("Output Size."); + TVM_ATTR_FIELD(roi) + .set_default(NullValue >()) + .describe("Region of Interest for coordinate transformation mode 'tf_crop_and_resize'"); TVM_ATTR_FIELD(layout).set_default("NCDHW").describe( "Dimension ordering of input data. Can be 'NCDHW', 'NDHWC', etc." "'N', 'C', 'D', 'H', 'W' stands for batch, channel, depth, height, and width" @@ -163,6 +184,9 @@ struct Resize3DAttrs : public tvm::AttrsNode { TVM_ATTR_FIELD(cubic_exclude) .set_default(0) .describe("Flag to exclude exterior of the image during tricubic interpolation"); + TVM_ATTR_FIELD(extrapolation_value) + .set_default(0.0) + .describe("Value to return when roi is outside of the image"); TVM_ATTR_FIELD(out_dtype).set_default(NullValue()).describe("Output data type."); } }; diff --git a/python/tvm/relay/frontend/onnx.py b/python/tvm/relay/frontend/onnx.py index 25da10690ecb..55a2e253e596 100644 --- a/python/tvm/relay/frontend/onnx.py +++ b/python/tvm/relay/frontend/onnx.py @@ -2610,13 +2610,13 @@ def _impl_v10(cls, inputs, attr, params): out = None if ndims == 3: out_size = fold_constant(_op.strided_slice(size, [2], [3])) - out = _op.image.resize1d(inputs[0], out_size, "NCW", method, "asymmetric") + out = _op.image.resize1d(inputs[0], out_size, None, "NCW", method, "asymmetric") elif ndims == 4: out_size = fold_constant(_op.strided_slice(size, [2], [4])) - out = _op.image.resize2d(inputs[0], out_size, "NCHW", method, "asymmetric") + out = _op.image.resize2d(inputs[0], out_size, None, "NCHW", method, "asymmetric") elif ndims == 5: out_size = fold_constant(_op.strided_slice(size, [2], [5])) - out = _op.image.resize3d(inputs[0], out_size, "NCDHW", method, "asymmetric") + out = _op.image.resize3d(inputs[0], out_size, None, "NCDHW", method, "asymmetric") else: raise NotImplementedError("Resize only supports 3, 4, or 5 dims") return out @@ -2663,6 +2663,9 @@ def v11_13_common(cls, inputs, size, attr, params): they handle the passing of scale and size. This utility provides the implementation for both """ + roi = inputs[1] + if roi is not None and infer_shape(roi)[0] == 0: + roi = None ndims = len(infer_shape(inputs[0])) mode = attr.get("mode").decode("ascii") if mode == "nearest": @@ -2680,27 +2683,55 @@ def v11_13_common(cls, inputs, size, attr, params): nearest_mode = attr.get("nearest_mode", b"round_prefer_floor").decode("ascii") alpha = attr.get("cubic_coeff_a", -0.75) exclude = attr.get("exclude_outside", 0) + extrapolation_value = attr.get("extrapolation_value", 0.0) out_size = fold_constant(_op.strided_slice(size, [2], [4])) out = None if ndims == 3: out_size = fold_constant(_op.strided_slice(size, [2], [3])) out = _op.image.resize1d( - inputs[0], out_size, "NCW", method, coord_trans, nearest_mode, alpha, exclude + inputs[0], + out_size, + roi, + "NCW", + method, + coord_trans, + nearest_mode, + alpha, + exclude, + extrapolation_value, ) elif ndims == 4: out_size = fold_constant(_op.strided_slice(size, [2], [4])) out = _op.image.resize2d( - inputs[0], out_size, "NCHW", method, coord_trans, nearest_mode, alpha, exclude + inputs[0], + out_size, + roi, + "NCHW", + method, + coord_trans, + nearest_mode, + alpha, + exclude, + extrapolation_value, ) elif ndims == 5: out_size = fold_constant(_op.strided_slice(size, [2], [5])) out = _op.image.resize3d( - inputs[0], out_size, "NCDHW", method, coord_trans, nearest_mode, alpha, exclude + inputs[0], + out_size, + roi, + "NCDHW", + method, + coord_trans, + nearest_mode, + alpha, + exclude, + extrapolation_value, ) else: raise NotImplementedError("Resize only supports 3, 4, or 5 dims") - + print(out) return out diff --git a/python/tvm/relay/op/dyn/image/_image.py b/python/tvm/relay/op/dyn/image/_image.py index 5e97d2461100..faebde02b2ca 100644 --- a/python/tvm/relay/op/dyn/image/_image.py +++ b/python/tvm/relay/op/dyn/image/_image.py @@ -28,16 +28,21 @@ # resize @reg.register_compute("dyn.image.resize2d") def compute_resize2d(attrs, inputs, out_type): + """ + Compute function calls into topi + """ layout = attrs.layout method = attrs.method coord_trans = attrs.coordinate_transformation_mode rounding_method = attrs.rounding_method cubic_alpha = attrs.cubic_alpha cubic_exclude = attrs.cubic_exclude + extrapolation_value = attrs.extrapolation_value out_dtype = attrs.out_dtype return [ tvm.topi.image.resize2d( inputs[0], + inputs[2], inputs[1], layout, method, @@ -45,6 +50,7 @@ def compute_resize2d(attrs, inputs, out_type): rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, out_type.shape, ) diff --git a/python/tvm/relay/op/image/_image.py b/python/tvm/relay/op/image/_image.py index ec24ff76b90e..d9929869beb1 100644 --- a/python/tvm/relay/op/image/_image.py +++ b/python/tvm/relay/op/image/_image.py @@ -34,16 +34,19 @@ def compute_resize1d(attrs, inputs, out_type): """compute definition for resize1d op""" size = attrs.size + roi = attrs.roi layout = attrs.layout method = attrs.method coord_trans = attrs.coordinate_transformation_mode rounding_method = attrs.rounding_method cubic_alpha = attrs.cubic_alpha cubic_exclude = attrs.cubic_exclude + extrapolation_value = attrs.extrapolation_value out_dtype = attrs.out_dtype return [ topi.image.resize1d( inputs[0], + roi, size, layout, method, @@ -51,6 +54,7 @@ def compute_resize1d(attrs, inputs, out_type): rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) ] @@ -128,16 +132,19 @@ def resize1d_shape_func(attrs, inputs, _): def compute_resize2d(attrs, inputs, out_type): """compute definition for resize2d op""" size = attrs.size + roi = attrs.roi layout = attrs.layout method = attrs.method coord_trans = attrs.coordinate_transformation_mode rounding_method = attrs.rounding_method cubic_alpha = attrs.cubic_alpha cubic_exclude = attrs.cubic_exclude + extrapolation_value = attrs.extrapolation_value out_dtype = attrs.out_dtype return [ topi.image.resize2d( inputs[0], + roi, size, layout, method, @@ -145,6 +152,7 @@ def compute_resize2d(attrs, inputs, out_type): rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) ] @@ -225,16 +233,19 @@ def resize2d_shape_func(attrs, inputs, _): def compute_resize3d(attrs, inputs, out_type): """compute definition for resize3d op""" size = attrs.size + roi = attrs.roi layout = attrs.layout method = attrs.method coord_trans = attrs.coordinate_transformation_mode rounding_method = attrs.rounding_method cubic_alpha = attrs.cubic_alpha cubic_exclude = attrs.cubic_exclude + extrapolation_value = attrs.extrapolation_value out_dtype = attrs.out_dtype return [ topi.image.resize3d( inputs[0], + roi, size, layout, method, @@ -242,6 +253,7 @@ def compute_resize3d(attrs, inputs, out_type): rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) ] diff --git a/python/tvm/relay/op/image/image.py b/python/tvm/relay/op/image/image.py index 7f5bd80159f9..c328e545b3eb 100644 --- a/python/tvm/relay/op/image/image.py +++ b/python/tvm/relay/op/image/image.py @@ -17,18 +17,20 @@ """Image operations.""" from . import _make from ..dyn.image import _make as _dyn_make -from ...expr import Expr, Constant +from ...expr import Expr, Constant, const def resize1d( data, size, + roi=None, layout="NCW", method="linear", coordinate_transformation_mode="half_pixel", rounding_method="", cubic_alpha=-0.5, cubic_exclude=0, + extrapolation_value=0.0, out_dtype=None, ): """Image resize1d operator. @@ -49,6 +51,9 @@ def resize1d( size: Tuple of Int or Expr The out size to which the image will be resized. + roi: Tuple of Float or Expr + The region of interest for tf_crop_and_resize. + layout : str, optional Layout of the input. @@ -69,7 +74,10 @@ def resize1d( Spline Coefficient for cubic interpolation cubic_exclude: int - Flag to exclude exterior of the image during cubic interpolation + Flag to exclude exterior of the image during cubic interpolation + + extrapolation_value: float + Fill value to use when roi is outside of the image out_dtype : str, optional Type to return. If left None returns the same type as input. @@ -79,19 +87,27 @@ def resize1d( result: relay.Expr The resized result. """ + if roi is None: + roi = [0.0] * 6 if isinstance(size, Constant): size = list(size.data.numpy().astype("int32")) - if isinstance(size, Expr): - raise NotImplementedError("dyn.resize1d is not yet implemented, got size", size) + if isinstance(roi, Constant): + roi = list(roi.data.numpy().astype("int32")) + if isinstance(size, Expr) or isinstance(roi, Expr): + raise NotImplementedError( + "dyn.resize1d is not yet implemented, got size", size, "and roi", roi + ) return _make.resize1d( data, size, + roi, layout, method, coordinate_transformation_mode, rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) @@ -99,12 +115,14 @@ def resize1d( def resize2d( data, size, + roi=None, layout="NCHW", method="linear", coordinate_transformation_mode="half_pixel", rounding_method="", cubic_alpha=-0.5, cubic_exclude=0, + extrapolation_value=0.0, out_dtype=None, ): """Image resize2d operator. @@ -125,6 +143,9 @@ def resize2d( size: Tuple of Int or Expr The out size to which the image will be resized. + roi: Tuple of Float or Expr + The region of interest for tf_crop_and_resize. + layout : str, optional Layout of the input. @@ -145,7 +166,10 @@ def resize2d( Spline Coefficient for bicubic interpolation cubic_exclude: int - Flag to exclude exterior of the image during bicubic interpolation + Flag to exclude exterior of the image during bicubic interpolation + + extrapolation_value: float + Fill value to use when roi is outside of the image out_dtype : str, optional Type to return. If left None returns the same type as input. @@ -155,29 +179,41 @@ def resize2d( result: relay.Expr The resized result. """ + if roi is None: + roi = [0.0] * 8 if isinstance(size, Constant): size = list(size.data.numpy().astype("int32")) - if isinstance(size, Expr): + if isinstance(roi, Constant): + roi = list(roi.data.numpy().astype("float32")) + if isinstance(size, Expr) or isinstance(roi, Expr): + if not isinstance(size, Expr): + size = const(size, "int64") + if not isinstance(roi, Expr): + roi = const(roi, "float32") return _dyn_make.resize2d( data, size, + roi, layout, method, coordinate_transformation_mode, rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) return _make.resize2d( data, size, + roi, layout, method, coordinate_transformation_mode, rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) @@ -185,12 +221,14 @@ def resize2d( def resize3d( data, size, + roi=None, layout="NCDHW", method="linear", coordinate_transformation_mode="half_pixel", rounding_method="", cubic_alpha=-0.5, cubic_exclude=0, + extrapolation_value=0.0, out_dtype=None, ): """Image resize3d operator. @@ -211,6 +249,9 @@ def resize3d( size: Tuple of Int or Expr The out size to which the image will be resized. + roi: Tuple of Float or Expr + The region of interest for tf_crop_and_resize. + layout : str, optional Layout of the input. @@ -231,7 +272,10 @@ def resize3d( Spline Coefficient for cubic interpolation cubic_exclude: int - Flag to exclude exterior of the image during cubic interpolation + Flag to exclude exterior of the image during cubic interpolation + + extrapolation_value: float + Fill value to use when roi is outside of the image out_dtype : str, optional Type to return. If left None returns the same type as input. @@ -241,19 +285,27 @@ def resize3d( result: relay.Expr The resized result. """ + if roi is None: + roi = [0.0] * 10 if isinstance(size, Constant): size = list(size.data.numpy().astype("int32")) - if isinstance(size, Expr): - raise NotImplementedError("dyn.resize3d is not yet implemented, got size", size) + if isinstance(roi, Constant): + roi = list(roi.data.numpy().astype("int32")) + if isinstance(size, Expr) or isinstance(roi, Expr): + raise NotImplementedError( + "dyn.resize3d is not yet implemented, got size", size, "and roi", roi + ) return _make.resize3d( data, size, + roi, layout, method, coordinate_transformation_mode, rounding_method, cubic_alpha, cubic_exclude, + extrapolation_value, out_dtype, ) diff --git a/python/tvm/topi/image/resize.py b/python/tvm/topi/image/resize.py index d1abffd12972..07472ed454b5 100644 --- a/python/tvm/topi/image/resize.py +++ b/python/tvm/topi/image/resize.py @@ -119,7 +119,7 @@ def get_3d_pixel(data, layout, image_depth, image_height, image_width, n, c, z, return data(n, c, z, y, x, cc).astype("float") -def get_inx(x, image_width, target_width, coordinate_transformation_mode): +def get_inx(x, image_width, target_width, coordinate_transformation_mode, start_x=0, end_x=-1): """Infer input x from output x with various coordinate transformation methods""" scale_x = te.div(image_width.astype("float"), target_width.astype("float")) if coordinate_transformation_mode == "half_pixel": @@ -132,6 +132,13 @@ def get_inx(x, image_width, target_width, coordinate_transformation_mode): in_x = te.if_then_else(target_width > 1, (x + 0.5) * scale_x - 0.5, 0.0) elif coordinate_transformation_mode == "tf_half_pixel_for_nn": in_x = (x + 0.5) * scale_x + elif coordinate_transformation_mode == "tf_crop_and_resize": + in_x = te.if_then_else( + target_width > 1, + start_x * (image_width - 1) + + x * (end_x - start_x) * (image_width - 1).astype("float") / (target_width - 1), + 0.5 * (start_x + end_x) * (image_width - 1), + ) else: raise ValueError( "Unsupported coordinate_transformation_mode: {}".format(coordinate_transformation_mode) @@ -184,12 +191,13 @@ def _cubic_kernel(inputs, w): def _resize_1d( indices, data, + roi, image_width, target_width, boxes=None, box_indices=None, method=None, - extrapolation_value=None, + extrapolation_value=0.0, layout="NCW", coordinate_transformation_mode="align_corners", rounding_method="", @@ -230,6 +238,9 @@ def _resize_1d( layout: string, optional "NCW", "NWC", or "NCWc". + method: string, optional + method of interpolation ("nearest", "linear", "bicubic") + coordinate_transformation_mode: string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. @@ -243,7 +254,7 @@ def _resize_1d( alpha: float, optional Bicubic spline coefficient - exclude_oiutside: bool, optional: + exclude_outside: bool, optional: Exclude values outside the image fdor bicubic interpolation out_dtype: string, optional @@ -272,6 +283,8 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): image_width, target_width, coordinate_transformation_mode, + roi[2], + roi[5], ) if method == "nearest_neighbor": @@ -347,7 +360,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): else: raise ValueError("Unknown resize method:", method) - if extrapolation_value is not None: + if coordinate_transformation_mode == "tf_crop_and_resize": # use extrapolation_value if in_x is out of boundary value = tvm.tir.if_then_else( in_x < 0, @@ -359,6 +372,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): def resize1d( data, + roi, size, layout="NCW", method="linear", @@ -366,6 +380,7 @@ def resize1d( rounding_method="", bicubic_alpha=-0.5, bicubic_exclude=0, + extrapolation_value=0.0, out_dtype=None, output_shape=None, ): @@ -390,8 +405,26 @@ def resize1d( Refer to the ONNX Resize operator specification for details. Available options are "half_pixel", "align_corners" and "asymmetric". - method: {"linear", "nearest_neighbor", "cubic"} - Method to be used for resizing. + method: string, optional + method of interpolation ("nearest", "linear", "bicubic") + + coordinate_transformation_mode: string, optional + Describes how to transform the coordinate in the resized tensor + to the coordinate in the original tensor. + Refer to the ONNX Resize operator specification for details. + Available options are "half_pixel", "align_corners" and "asymmetric". + + rounding_method: + Method for rounding coordinate locations + + bicubic_alpha: float, optional + Bicubic spline coefficient + + bicubic_exclude: bool, optional: + Exclude values outside the image fdor bicubic interpolation + + extrapolation_value: float, optional + Value used for extrapolation, when applicable. out_dtype: string, optional Type to return. If left None will be same as input type. @@ -438,6 +471,7 @@ def compute_func(*indices): return _resize_1d( indices, data, + roi, in_w, size[0], method=method, @@ -446,6 +480,7 @@ def compute_func(*indices): rounding_method=rounding_method, alpha=bicubic_alpha, exclude_outside=bicubic_exclude, + extrapolation_value=extrapolation_value, out_dtype=out_dtype, ) @@ -455,6 +490,7 @@ def compute_func(*indices): def _resize_2d( indices, data, + roi, image_height, image_width, target_height, @@ -462,7 +498,7 @@ def _resize_2d( boxes=None, box_indices=None, method=None, - extrapolation_value=None, + extrapolation_value=0.0, layout="NCHW", coordinate_transformation_mode="align_corners", rounding_method="", @@ -499,6 +535,9 @@ def _resize_2d( A 2-D tensor of shape [num_boxes, 4]. Each row of the tensor specifies the coordinates of a box. + method: string, optional + method of interpolation ("nearest", "linear", "bicubic") + box_indices : tvm.te.Tensor, optional A 1-D tensor of shape [num_boxes], box_indices[i] specifies the data that the i-th box refers to. @@ -522,7 +561,7 @@ def _resize_2d( alpha: float, optional Bicubic spline coefficient - exclude_oiutside: bool, optional: + exclude_outside: bool, optional: Exclude values outside the image fdor bicubic interpolation out_dtype: string, optional @@ -555,8 +594,10 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): in_y = y1 * (image_height - 1) + h_scale * y in_x = x1 * (image_width - 1) + w_scale * x else: - in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode) - in_y = get_inx(y, image_height, target_height, coordinate_transformation_mode) + in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[3], roi[7]) + in_y = get_inx( + y, image_height, target_height, coordinate_transformation_mode, roi[2], roi[6] + ) if method == "nearest_neighbor": if rounding_method == "": @@ -657,7 +698,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): else: raise ValueError("Unknown resize method:", method) - if extrapolation_value is not None: + if coordinate_transformation_mode == "tf_crop_and_resize": out = tvm.tir.if_then_else( in_y < 0, extrapolation_value, @@ -674,6 +715,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): def resize2d( data, + roi, size, layout="NCHW", method="linear", @@ -681,6 +723,7 @@ def resize2d( rounding_method="", bicubic_alpha=-0.5, bicubic_exclude=0, + extrapolation_value=0.0, out_dtype=None, output_shape=None, ): @@ -705,8 +748,26 @@ def resize2d( Refer to the ONNX Resize operator specification for details. Available options are "half_pixel", "align_corners" and "asymmetric". - method: {"linear", "nearest_neighbor", "cubic"} - Method to be used for resizing. + method: string, optional + method of interpolation ("nearest", "linear", "bicubic") + + coordinate_transformation_mode: string, optional + Describes how to transform the coordinate in the resized tensor + to the coordinate in the original tensor. + Refer to the ONNX Resize operator specification for details. + Available options are "half_pixel", "align_corners" and "asymmetric". + + rounding_method: + Method for rounding coordinate locations + + bicubic_alpha: float, optional + Bicubic spline coefficient + + bicubic_exclude: bool, optional: + Exclude values outside the image fdor bicubic interpolation + + extrapolation_value: float, optional + Value used for extrapolation, when applicable. out_dtype: string, optional Type to return. If left None will be same as input type. @@ -753,6 +814,7 @@ def compute_func(*indices): return _resize_2d( indices, data, + roi, in_h, in_w, size[0], @@ -763,6 +825,7 @@ def compute_func(*indices): rounding_method=rounding_method, alpha=bicubic_alpha, exclude_outside=bicubic_exclude, + extrapolation_value=extrapolation_value, out_dtype=out_dtype, ) @@ -776,7 +839,7 @@ def crop_and_resize( crop_size, layout="NCHW", method="bilinear", - extrapolation_value=0, + extrapolation_value=None, out_dtype=None, ): """Perform crop and resize operation on the data. @@ -865,6 +928,7 @@ def compute_func(*indices): def _resize_3d( indices, data, + roi, image_depth, image_height, image_width, @@ -874,7 +938,7 @@ def _resize_3d( boxes=None, box_indices=None, method=None, - extrapolation_value=None, + extrapolation_value=0.0, layout="NCHW", coordinate_transformation_mode="align_corners", rounding_method="", @@ -921,6 +985,9 @@ def _resize_3d( A 1-D tensor of shape [num_boxes], box_indices[i] specifies the data that the i-th box refers to. + method: string, optional + method of interpolation ("nearest", "linear", "bicubic") + extrapolation_value: float, optional Value used for extrapolation, when applicable. @@ -964,9 +1031,9 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): if boxes is not None: # TODO(mbrookhart): Find an example of this raise NotImplementedError("resize1d with image boxes not yet implemented") - in_z = get_inx(z, image_depth, target_depth, coordinate_transformation_mode) - in_y = get_inx(y, image_height, target_height, coordinate_transformation_mode) - in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode) + in_z = get_inx(z, image_depth, target_depth, coordinate_transformation_mode, roi[4], roi[9]) + in_y = get_inx(y, image_height, target_height, coordinate_transformation_mode, roi[3], roi[8]) + in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[2], roi[7]) if method == "nearest_neighbor": if rounding_method == "": @@ -1090,7 +1157,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): else: raise ValueError("Unknown resize method:", method) - if extrapolation_value is not None: + if coordinate_transformation_mode == "tf_crop_and_resize": out = tvm.tir.if_then_else( in_z < 0, extrapolation_value, @@ -1112,6 +1179,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): def resize3d( data, + roi, size, layout="NCDHW", method="linear", @@ -1119,6 +1187,7 @@ def resize3d( rounding_method="", bicubic_alpha=-0.5, bicubic_exclude=0, + extrapolation_value=0.0, out_dtype=None, output_shape=None, ): @@ -1137,14 +1206,26 @@ def resize3d( layout: string, optional "NCDHW", "NDHWC", or "NCDHWc". + method: string, optional + method of interpolation ("nearest", "linear", "bicubic") + coordinate_transformation_mode: string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. Refer to the ONNX Resize operator specification for details. Available options are "half_pixel", "align_corners" and "asymmetric". - method: {"linear", "nearest_neighbor", "cubic"} - Method to be used for resizing. + rounding_method: + Method for rounding coordinate locations + + bicubic_alpha: float, optional + Bicubic spline coefficient + + bicubic_exclude: bool, optional: + Exclude values outside the image fdor bicubic interpolation + + extrapolation_value: float, optional + Value used for extrapolation, when applicable. out_dtype: string, optional Type to return. If left None will be same as input type. @@ -1185,6 +1266,7 @@ def compute_func(*indices): return _resize_3d( indices, data, + roi, in_d, in_h, in_w, @@ -1197,6 +1279,7 @@ def compute_func(*indices): rounding_method=rounding_method, alpha=bicubic_alpha, exclude_outside=bicubic_exclude, + extrapolation_value=extrapolation_value, out_dtype=out_dtype, ) diff --git a/python/tvm/topi/nn/upsampling.py b/python/tvm/topi/nn/upsampling.py index 36b9349a139d..efd054aa593e 100644 --- a/python/tvm/topi/nn/upsampling.py +++ b/python/tvm/topi/nn/upsampling.py @@ -96,6 +96,7 @@ def upsampling( method = method[2:] return topi.image.resize2d( data, + [0.0] * 8, reshape_size, layout=layout, method=method, @@ -194,6 +195,7 @@ def upsampling3d( method = method[3:] return topi.image.resize3d( data, + [0.0] * 10, resize_shape, layout=layout, method=method, diff --git a/src/relay/op/dyn/image/resize.cc b/src/relay/op/dyn/image/resize.cc index 002105f4d565..1f5f6b43763f 100644 --- a/src/relay/op/dyn/image/resize.cc +++ b/src/relay/op/dyn/image/resize.cc @@ -35,8 +35,8 @@ TVM_REGISTER_NODE_TYPE(Resize2DAttrs); bool Resize2DRel(const Array& types, int num_inputs, const Attrs& attrs, const TypeReporter& reporter) { - // {data, size, out} - ICHECK_EQ(types.size(), 3); + // {data, size, roi, out} + ICHECK_EQ(types.size(), 4); const auto* data = types[0].as(); if (data == nullptr) return false; @@ -60,15 +60,15 @@ bool Resize2DRel(const Array& types, int num_inputs, const Attrs& attrs, } // assign output type - reporter->Assign(types[2], TensorType(layout_converter.BackwardShape(oshape), out_dtype)); + reporter->Assign(types[3], TensorType(layout_converter.BackwardShape(oshape), out_dtype)); return true; } // Positional relay function to create image operator // used by frontend FFI. -Expr MakeResize2D(Expr data, Expr size, String layout, String method, +Expr MakeResize2D(Expr data, Expr size, Expr roi, String layout, String method, String coordinate_transformation_mode, String rounding_method, double cubic_alpha, - double cubic_exclude, DataType out_dtype) { + double cubic_exclude, double extrapolation_value, DataType out_dtype) { auto attrs = make_object(); attrs->layout = std::move(layout); attrs->method = std::move(method); @@ -76,9 +76,10 @@ Expr MakeResize2D(Expr data, Expr size, String layout, String method, attrs->rounding_method = rounding_method; attrs->cubic_alpha = cubic_alpha; attrs->cubic_exclude = cubic_exclude; + attrs->extrapolation_value = extrapolation_value; attrs->out_dtype = out_dtype; static const Op& op = Op::Get("dyn.image.resize2d"); - return Call(op, {data, size}, Attrs(attrs), {}); + return Call(op, {data, size, roi}, Attrs(attrs), {}); } TVM_REGISTER_GLOBAL("relay.op.dyn.image._make.resize2d").set_body_typed(MakeResize2D); @@ -101,9 +102,10 @@ RELAY_REGISTER_OP("dyn.image.resize2d") (batch_size, size[0], size[1], channels) )code" TVM_ADD_FILELINE) .set_attrs_type() - .set_num_inputs(2) + .set_num_inputs(3) .add_argument("data", "Tensor", "The input tensor.") .add_argument("size", "Tensor", "The output size tensor.") + .add_argument("roi", "Tensor", "The region of interest for tf_crop_and_resize.") .set_support_level(5) .add_type_rel("DynResize2D", Resize2DRel) .set_attr("TOpPattern", kInjective); diff --git a/src/relay/op/image/resize.cc b/src/relay/op/image/resize.cc index ee779841505c..7679550fc7e5 100644 --- a/src/relay/op/image/resize.cc +++ b/src/relay/op/image/resize.cc @@ -68,6 +68,8 @@ bool Resize1DRel(const Array& types, int num_inputs, const Attrs& attrs, const Resize1DAttrs* param = attrs.as(); ICHECK(param != nullptr); + ICHECK(param->size.size() == 1); + ICHECK(param->roi.size() == 6); const Layout in_layout(param->layout); auto layout_converter = tir::BijectiveLayout(in_layout, kNCW); ICHECK(layout_converter.defined()) @@ -89,17 +91,20 @@ bool Resize1DRel(const Array& types, int num_inputs, const Attrs& attrs, // Positional relay function to create image operator // used by frontend FFI. -Expr MakeResize1D(Expr data, Array size, String layout, String method, - String coordinate_transformation_mode, String rounding_method, double cubic_alpha, - int cubic_exclude, DataType out_dtype) { +Expr MakeResize1D(Expr data, Array size, Array roi, String layout, + String method, String coordinate_transformation_mode, String rounding_method, + double cubic_alpha, int cubic_exclude, double extrapolation_value, + DataType out_dtype) { auto attrs = make_object(); attrs->size = std::move(size); + attrs->roi = std::move(roi); attrs->layout = std::move(layout); attrs->method = std::move(method); attrs->coordinate_transformation_mode = coordinate_transformation_mode; attrs->rounding_method = rounding_method; attrs->cubic_alpha = cubic_alpha; attrs->cubic_exclude = cubic_exclude; + attrs->extrapolation_value = extrapolation_value; attrs->out_dtype = out_dtype; static const Op& op = Op::Get("image.resize1d"); return Call(op, {data}, Attrs(attrs), {}); @@ -141,6 +146,8 @@ bool Resize2DRel(const Array& types, int num_inputs, const Attrs& attrs, const Resize2DAttrs* param = attrs.as(); ICHECK(param != nullptr); + ICHECK(param->size.size() == 2); + ICHECK(param->roi.size() == 8); const Layout in_layout(param->layout); auto layout_converter = tir::BijectiveLayout(in_layout, kNCHW); ICHECK(layout_converter.defined()) @@ -163,17 +170,20 @@ bool Resize2DRel(const Array& types, int num_inputs, const Attrs& attrs, // Positional relay function to create image operator // used by frontend FFI. -Expr MakeResize2D(Expr data, Array size, String layout, String method, - String coordinate_transformation_mode, String rounding_method, double cubic_alpha, - int cubic_exclude, DataType out_dtype) { +Expr MakeResize2D(Expr data, Array size, Array roi, String layout, + String method, String coordinate_transformation_mode, String rounding_method, + double cubic_alpha, int cubic_exclude, double extrapolation_value, + DataType out_dtype) { auto attrs = make_object(); attrs->size = std::move(size); + attrs->roi = std::move(roi); attrs->layout = std::move(layout); attrs->method = std::move(method); attrs->coordinate_transformation_mode = coordinate_transformation_mode; attrs->rounding_method = rounding_method; attrs->cubic_alpha = cubic_alpha; attrs->cubic_exclude = cubic_exclude; + attrs->extrapolation_value = extrapolation_value; attrs->out_dtype = out_dtype; static const Op& op = Op::Get("image.resize2d"); return Call(op, {data}, Attrs(attrs), {}); @@ -215,6 +225,8 @@ bool Resize3DRel(const Array& types, int num_inputs, const Attrs& attrs, const Resize3DAttrs* param = attrs.as(); ICHECK(param != nullptr); + ICHECK(param->size.size() == 3); + ICHECK(param->roi.size() == 10); const Layout in_layout(param->layout); auto layout_converter = tir::BijectiveLayout(in_layout, kNCDHW); ICHECK(layout_converter.defined()) @@ -238,17 +250,20 @@ bool Resize3DRel(const Array& types, int num_inputs, const Attrs& attrs, // Positional relay function to create image operator // used by frontend FFI. -Expr MakeResize3D(Expr data, Array size, String layout, String method, - String coordinate_transformation_mode, String rounding_method, double cubic_alpha, - int cubic_exclude, DataType out_dtype) { +Expr MakeResize3D(Expr data, Array size, Array roi, String layout, + String method, String coordinate_transformation_mode, String rounding_method, + double cubic_alpha, int cubic_exclude, double extrapolation_value, + DataType out_dtype) { auto attrs = make_object(); attrs->size = std::move(size); + attrs->roi = std::move(roi); attrs->layout = std::move(layout); attrs->method = std::move(method); attrs->coordinate_transformation_mode = coordinate_transformation_mode; attrs->rounding_method = rounding_method; attrs->cubic_alpha = cubic_alpha; attrs->cubic_exclude = cubic_exclude; + attrs->extrapolation_value = extrapolation_value; attrs->out_dtype = out_dtype; static const Op& op = Op::Get("image.resize3d"); return Call(op, {data}, Attrs(attrs), {}); diff --git a/src/relay/op/make_op.h b/src/relay/op/make_op.h index 43ce6656cdc0..d02aed79ac78 100644 --- a/src/relay/op/make_op.h +++ b/src/relay/op/make_op.h @@ -101,9 +101,10 @@ Expr MakeZeros(Array shape, DataType dtype); Expr MakeOneHot(Expr indices, Expr on_value, Expr off_value, int depth, int axis, DataType dtype); -Expr MakeResize2D(Expr data, Array size, String layout, String method, - String coordinate_transformation_mode, String rounding_method, double cubic_alpha, - int cubic_exclude, DataType out_dtype); +Expr MakeResize2D(Expr data, Array size, Array roi, String layout, + String method, String coordinate_transformation_mode, String rounding_method, + double cubic_alpha, int cubic_exclude, double extrapolation_value, + DataType out_dtype); Expr MakeSparseToDense(Expr indices, Array output_shape, Expr values, Expr default_value); diff --git a/src/relay/transforms/dynamic_to_static.cc b/src/relay/transforms/dynamic_to_static.cc index 751271d2add3..f3c53cfc8bc0 100644 --- a/src/relay/transforms/dynamic_to_static.cc +++ b/src/relay/transforms/dynamic_to_static.cc @@ -119,16 +119,24 @@ class DynamicToStaticMutator : public MixedModeMutator { [this](const CallNode* call_node) { auto args = PrepareArgs(call_node); if (const ConstantNode* size = args[1].as()) { - const Resize2DAttrs* param = call_node->attrs.as(); - ICHECK(param); - auto size_int = ToVector(size->data); - Array size_prim; - for (size_t i = 0; i < size_int.size(); ++i) { - size_prim.push_back(size_int[i]); + if (const ConstantNode* roi = args[2].as()) { + const Resize2DAttrs* param = call_node->attrs.as(); + ICHECK(param); + auto size_int = ToVector(size->data); + Array size_prim; + for (size_t i = 0; i < size_int.size(); ++i) { + size_prim.push_back(size_int[i]); + } + auto roi_vec = ToFloatVector(roi->data); + Array roi_prim; + for (size_t i = 0; i < roi_vec.size(); ++i) { + roi_prim.push_back(roi_vec[i]); + } + return MakeResize2D(call_node->args[0], size_prim, roi_prim, param->layout, + param->method, param->coordinate_transformation_mode, + param->rounding_method, param->cubic_alpha, param->cubic_exclude, + param->extrapolation_value, param->out_dtype); } - return MakeResize2D(call_node->args[0], size_prim, param->layout, param->method, - param->coordinate_transformation_mode, param->rounding_method, - param->cubic_alpha, param->cubic_exclude, param->out_dtype); } return Expr(nullptr); }}, diff --git a/src/relay/transforms/pattern_utils.h b/src/relay/transforms/pattern_utils.h index 03b8ee6937a7..69ad20a7ceaf 100644 --- a/src/relay/transforms/pattern_utils.h +++ b/src/relay/transforms/pattern_utils.h @@ -450,6 +450,23 @@ static inline Array ToVector(const runtime::NDArray& array) { return out; } +/*! + * \brief Convert a NDArray with type int or float to Array. + * \param array Input NDArray + * \return Converted Array. + */ +static inline Array ToFloatVector(const runtime::NDArray& array) { + size_t ndim = array.Shape().size(); + ICHECK_EQ(ndim, 1) << "This function should only be used for 1D NDArrays"; + size_t len = array.Shape().front(); + Array out; + for (size_t i = 0; i < len; ++i) { + long double elem_val = ToScalar(array, i); + out.push_back(FloatImm(DataType::Float(32), static_cast(elem_val))); + } + return out; +} + /*! * \brief Convert a NDArray with type int or float to Array>. * \param array Input NDArray diff --git a/tests/python/frontend/onnx/test_forward.py b/tests/python/frontend/onnx/test_forward.py index f8870edcb6d1..e9298eeb9cc7 100644 --- a/tests/python/frontend/onnx/test_forward.py +++ b/tests/python/frontend/onnx/test_forward.py @@ -4958,7 +4958,7 @@ def verify_eyelike(indata): "test_reduce_sum_keepdims_random", "test_reduce_sum_negative_axes_keepdims_example", "test_reduce_sum_negative_axes_keepdims_random", - "test_resize_tf_crop_and_resize", + # "test_resize_tf_crop_and_resize", "test_rnn_seq_length", "test_round", "test_scan9_sum", From 11381f9d3ec8d86f8bac06d7f57ef0a0bf57ff64 Mon Sep 17 00:00:00 2001 From: Matthew Brookhart Date: Tue, 9 Nov 2021 09:52:58 -0700 Subject: [PATCH 3/6] change api use in the rest of the codebase really fix the tests --- python/tvm/relay/frontend/pytorch.py | 8 +++++--- python/tvm/relay/frontend/tensorflow_ops.py | 4 +++- python/tvm/relay/frontend/tflite.py | 2 +- python/tvm/topi/image/resize.py | 2 ++ tests/python/contrib/test_onnx.py | 1 + tests/python/relay/dyn/test_dynamic_op_level5.py | 2 +- tests/python/relay/test_any.py | 2 +- tests/python/relay/test_op_level5.py | 12 ++++++------ tests/python/relay/test_pass_dynamic_to_static.py | 2 +- tests/python/topi/python/test_topi_image.py | 2 ++ 10 files changed, 23 insertions(+), 14 deletions(-) diff --git a/python/tvm/relay/frontend/pytorch.py b/python/tvm/relay/frontend/pytorch.py index a17a10e7b398..2c9268ea245f 100644 --- a/python/tvm/relay/frontend/pytorch.py +++ b/python/tvm/relay/frontend/pytorch.py @@ -1822,7 +1822,7 @@ def upsample(inputs, input_types): def func(x): return _op.image.resize2d( - x, out_size, "NCHW", method, coord_trans, cubic_alpha=-0.75 + x, out_size, None, "NCHW", method, coord_trans, cubic_alpha=-0.75 ) if self.is_quantized_tensor(data): @@ -1854,7 +1854,7 @@ def upsample3d(inputs, input_types): else: coord_trans = "half_pixel" - return _op.image.resize3d(data, out_size, "NCDHW", method, coord_trans) + return _op.image.resize3d(data, out_size, None, "NCDHW", method, coord_trans) return upsample3d @@ -2186,7 +2186,9 @@ def interpolate(self, inputs, input_types): else: coord_trans = "half_pixel" - return _op.image.resize2d(data, out_size, "NCHW", method, coord_trans, cubic_alpha=-0.75) + return _op.image.resize2d( + data, out_size, None, "NCHW", method, coord_trans, cubic_alpha=-0.75 + ) def numel(self, inputs, input_types): return _op.ndarray_size(inputs[0]) diff --git a/python/tvm/relay/frontend/tensorflow_ops.py b/python/tvm/relay/frontend/tensorflow_ops.py index 26ea4f4dbc2a..df8b7438af88 100644 --- a/python/tvm/relay/frontend/tensorflow_ops.py +++ b/python/tvm/relay/frontend/tensorflow_ops.py @@ -1090,7 +1090,9 @@ def _impl(inputs, attr, params, mod): # Ignore the new attributes from TF2.0, for now. return AttrCvt( - op_name="resize2d", ignores=["Tdim", "half_pixel_centers"], extras={"method": method} + op_name="resize2d", + ignores=["Tdim", "half_pixel_centers"], + extras={"method": method, "roi": None}, )(inputs, attr) return _impl diff --git a/python/tvm/relay/frontend/tflite.py b/python/tvm/relay/frontend/tflite.py index 12beca5e898a..f0f20e19e2ef 100644 --- a/python/tvm/relay/frontend/tflite.py +++ b/python/tvm/relay/frontend/tflite.py @@ -658,7 +658,7 @@ def _convert_resize(self, method, op): if bilinear_method and input_tensor.qnn_params: in_expr = self.dequantize(in_expr, input_tensor) out = _op.image.resize2d( - in_expr, target_size, "NHWC", method, coordinate_transformation_mode=coord_trans + in_expr, target_size, None, "NHWC", method, coordinate_transformation_mode=coord_trans ) if bilinear_method and output_tensor.qnn_params: out = self.quantize(out, output_tensor) diff --git a/python/tvm/topi/image/resize.py b/python/tvm/topi/image/resize.py index 07472ed454b5..d8927cee441f 100644 --- a/python/tvm/topi/image/resize.py +++ b/python/tvm/topi/image/resize.py @@ -910,6 +910,7 @@ def compute_func(*indices): return _resize_2d( indices, data, + [0.0] * 8, image_h, image_w, target_h, @@ -919,6 +920,7 @@ def compute_func(*indices): method=method, extrapolation_value=extrapolation_value, layout=layout, + coordinate_transformation_mode="tf_crop_and_resize", out_dtype=out_dtype, ) diff --git a/tests/python/contrib/test_onnx.py b/tests/python/contrib/test_onnx.py index 6f23228be68c..214166cebb9d 100644 --- a/tests/python/contrib/test_onnx.py +++ b/tests/python/contrib/test_onnx.py @@ -663,6 +663,7 @@ def verify_resize(dshape, outsize, method, coord_trans, rounding_method, dtype=" y = relay.image.resize2d( x, outsize, + None, layout="NCHW", method=method, coordinate_transformation_mode=coord_trans, diff --git a/tests/python/relay/dyn/test_dynamic_op_level5.py b/tests/python/relay/dyn/test_dynamic_op_level5.py index c29ea2cd392f..2eeeb1d828c9 100644 --- a/tests/python/relay/dyn/test_dynamic_op_level5.py +++ b/tests/python/relay/dyn/test_dynamic_op_level5.py @@ -51,7 +51,7 @@ def verify_resize2d(dshape, scale, method, layout): coord_trans = "asymmetric" if method == "nearest_neighbor" else "align_corners" z = relay.image.resize2d( - x, size_var, layout, method, coordinate_transformation_mode=coord_trans + x, size_var, None, layout, method, coordinate_transformation_mode=coord_trans ) zz = run_infer_type(z) diff --git a/tests/python/relay/test_any.py b/tests/python/relay/test_any.py index f42f7ad7ca69..c04520f8bee1 100644 --- a/tests/python/relay/test_any.py +++ b/tests/python/relay/test_any.py @@ -1563,7 +1563,7 @@ def verify_any_resize2d(data_shape, scale, layout, static_data_shape, ref_out_sh size = (data_shape[1] * scale, data_shape[2] * scale) else: size = (data_shape[2] * scale, data_shape[3] * scale) - y = relay.image.resize2d(data, size, layout) + y = relay.image.resize2d(data, size, None, layout) mod["main"] = relay.Function([data], y) data_np = np.random.uniform(size=static_data_shape).astype(dtype) check_result([data_np], mod, ref_out_shape, assert_shape=True) diff --git a/tests/python/relay/test_op_level5.py b/tests/python/relay/test_op_level5.py index c968c5a7f19f..6ad3b3827dce 100644 --- a/tests/python/relay/test_op_level5.py +++ b/tests/python/relay/test_op_level5.py @@ -40,7 +40,7 @@ def test_resize1d_infer_type(): assert zz.checked_type == relay.TensorType((n, c, tw), "int8") x = relay.var("x", relay.TensorType((n, c, w), "int8")) - z = relay.image.resize1d(x, (200,), "NCW", "linear", "align_corners") + z = relay.image.resize1d(x, (200,), None, "NCW", "linear", "align_corners") assert "size=" in z.astext() zz = run_infer_type(z) assert zz.checked_type == relay.TensorType((n, c, 200), "int8") @@ -83,7 +83,7 @@ def test_resize( ) x = relay.var("x", relay.TensorType(dshape, "float32")) z = relay.image.resize1d( - x, size, layout, interpolate_method, coordinate_transformation_mode=coord_trans + x, size, None, layout, interpolate_method, coordinate_transformation_mode=coord_trans ) assert "size=" in z.astext() zz = run_infer_type(z) @@ -104,7 +104,7 @@ def test_resize2d_infer_type(): assert zz.checked_type == relay.TensorType((n, c, th, tw), "int8") x = relay.var("x", relay.TensorType((n, c, h, w), "int8")) - z = relay.image.resize2d(x, (100, 200), "NCHW", "linear", "align_corners") + z = relay.image.resize2d(x, (100, 200), None, "NCHW", "linear", "align_corners") assert "size=" in z.astext() zz = run_infer_type(z) assert zz.checked_type == relay.TensorType((n, c, 100, 200), "int8") @@ -148,7 +148,7 @@ def test_resize( ) x = relay.var("x", relay.TensorType(dshape, "float32")) z = relay.image.resize2d( - x, size, layout, interpolate_method, coordinate_transformation_mode=coord_trans + x, size, None, layout, interpolate_method, coordinate_transformation_mode=coord_trans ) assert "size=" in z.astext() zz = run_infer_type(z) @@ -175,7 +175,7 @@ def test_resize3d_infer_type(): assert zz.checked_type == relay.TensorType((n, c, td, th, tw), "int8") x = relay.var("x", relay.TensorType((n, c, d, h, w), "int8")) - z = relay.image.resize3d(x, (10, 10, 20), "NCDHW", "linear", "align_corners") + z = relay.image.resize3d(x, (10, 10, 20), None, "NCDHW", "linear", "align_corners") assert "size=" in z.astext() zz = run_infer_type(z) assert zz.checked_type == relay.TensorType((n, c, 10, 10, 20), "int8") @@ -204,7 +204,7 @@ def test_resize( x_data, (scale, scale, scale), layout, interpolate_method, coord_trans ) x = relay.var("x", relay.TensorType(dshape, "float32")) - z = relay.image.resize3d(x, size, layout, interpolate_method, coord_trans) + z = relay.image.resize3d(x, size, None, layout, interpolate_method, coord_trans) assert "size=" in z.astext() zz = run_infer_type(z) assert zz.checked_type == relay.TensorType(ref_res.shape, "float32") diff --git a/tests/python/relay/test_pass_dynamic_to_static.py b/tests/python/relay/test_pass_dynamic_to_static.py index 5b61733bbd76..f523ad2a27fa 100644 --- a/tests/python/relay/test_pass_dynamic_to_static.py +++ b/tests/python/relay/test_pass_dynamic_to_static.py @@ -291,7 +291,7 @@ def verify_resize(shape, scale, method, layout): size_var = relay.var("size", relay.TensorType((len(size),), "float32")) coord_trans = "asymmetric" if method == "nearest_neighbor" else "align_corners" z = relay.image.resize2d( - x, size_var, layout, method, coordinate_transformation_mode=coord_trans + x, size_var, None, layout, method, coordinate_transformation_mode=coord_trans ) params = {"size": np.array(size).astype("float32")} diff --git a/tests/python/topi/python/test_topi_image.py b/tests/python/topi/python/test_topi_image.py index fe7fba52f1ee..aef723a6d6c6 100644 --- a/tests/python/topi/python/test_topi_image.py +++ b/tests/python/topi/python/test_topi_image.py @@ -49,6 +49,7 @@ def verify_resize2d( raise NotImplementedError("Layout not supported {} ".format(layout)) B = topi.image.resize2d( A, + [0.0] * 8, (out_height, out_width), layout=layout, coordinate_transformation_mode=coord_trans, @@ -127,6 +128,7 @@ def verify_resize3d( B = topi.image.resize3d( A, + [0.0] * 10, (out_depth, out_height, out_width), layout=layout, coordinate_transformation_mode=coordinate_transformation_mode, From 5192ac9f35f23d852bf1cba53b54661fc8e86ed9 Mon Sep 17 00:00:00 2001 From: Matthew Brookhart Date: Fri, 12 Nov 2021 16:55:02 -0700 Subject: [PATCH 4/6] respond to review comments, improve doc strings --- python/tvm/relay/frontend/onnx.py | 2 +- python/tvm/relay/op/image/image.py | 42 ++++++++----- python/tvm/topi/image/resize.py | 72 ++++++++++++++++------ tests/python/frontend/onnx/test_forward.py | 1 - 4 files changed, 82 insertions(+), 35 deletions(-) diff --git a/python/tvm/relay/frontend/onnx.py b/python/tvm/relay/frontend/onnx.py index 55a2e253e596..8b3582a8fe65 100644 --- a/python/tvm/relay/frontend/onnx.py +++ b/python/tvm/relay/frontend/onnx.py @@ -2731,7 +2731,7 @@ def v11_13_common(cls, inputs, size, attr, params): ) else: raise NotImplementedError("Resize only supports 3, 4, or 5 dims") - print(out) + return out diff --git a/python/tvm/relay/op/image/image.py b/python/tvm/relay/op/image/image.py index c328e545b3eb..53ef95751c1c 100644 --- a/python/tvm/relay/op/image/image.py +++ b/python/tvm/relay/op/image/image.py @@ -51,8 +51,11 @@ def resize1d( size: Tuple of Int or Expr The out size to which the image will be resized. - roi: Tuple of Float or Expr - The region of interest for tf_crop_and_resize. + roi: Tuple of Float or Expr, optional + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. layout : str, optional Layout of the input. @@ -62,9 +65,10 @@ def resize1d( coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor - to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - [half_pixel, align_corners, asymmetric] + to the coordinate in the original tensor. Defintions can be found + in topi/image/resize.py. + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -143,8 +147,11 @@ def resize2d( size: Tuple of Int or Expr The out size to which the image will be resized. - roi: Tuple of Float or Expr - The region of interest for tf_crop_and_resize. + roi: Tuple of Float or Expr, optional + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. layout : str, optional Layout of the input. @@ -154,9 +161,10 @@ def resize2d( coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor - to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - [half_pixel, align_corners, asymmetric] + to the coordinate in the original tensor. Defintions can be found + in topi/image/resize.py. + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -249,8 +257,11 @@ def resize3d( size: Tuple of Int or Expr The out size to which the image will be resized. - roi: Tuple of Float or Expr - The region of interest for tf_crop_and_resize. + roi: Tuple of Float or Expr, optional + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. layout : str, optional Layout of the input. @@ -260,9 +271,10 @@ def resize3d( coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor - to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - [half_pixel, align_corners, asymmetric] + to the coordinate in the original tensor. Defintions can be found + in topi/image/resize.py. + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method diff --git a/python/tvm/topi/image/resize.py b/python/tvm/topi/image/resize.py index d8927cee441f..6a5549ecc783 100644 --- a/python/tvm/topi/image/resize.py +++ b/python/tvm/topi/image/resize.py @@ -218,6 +218,12 @@ def _resize_1d( [batch, channel, in_width] or [batch, in_width, channel] + roi: Tuple of Float or Expr + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. + image_width : integer Input image width @@ -241,11 +247,11 @@ def _resize_1d( method: string, optional method of interpolation ("nearest", "linear", "bicubic") - coordinate_transformation_mode: string, optional + coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - Available options are "half_pixel", "align_corners" and "asymmetric". + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -393,6 +399,12 @@ def resize1d( [batch, channel in_width] or [batch in_width, channel] + roi: Tuple of Float or Expr + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. + size: Tuple Output resolution scale to @@ -408,11 +420,11 @@ def resize1d( method: string, optional method of interpolation ("nearest", "linear", "bicubic") - coordinate_transformation_mode: string, optional + coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - Available options are "half_pixel", "align_corners" and "asymmetric". + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: Method for rounding coordinate locations @@ -519,6 +531,12 @@ def _resize_2d( [batch, channel, in_height, in_width] or [batch, in_height, in_width, channel] + roi: Tuple of Float or Expr + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. + image_height : integer Input image height @@ -548,11 +566,11 @@ def _resize_2d( layout: string, optional "NCHW", "NHWC", or "NCHWc". - coordinate_transformation_mode: string, optional + coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - Available options are "half_pixel", "align_corners" and "asymmetric". + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -736,6 +754,12 @@ def resize2d( [batch, channel, in_height, in_width] or [batch, in_height, in_width, channel] + roi: Tuple of Float or Expr + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. + size: Tuple Output resolution scale to @@ -751,11 +775,11 @@ def resize2d( method: string, optional method of interpolation ("nearest", "linear", "bicubic") - coordinate_transformation_mode: string, optional + coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - Available options are "half_pixel", "align_corners" and "asymmetric". + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: Method for rounding coordinate locations @@ -961,6 +985,12 @@ def _resize_3d( [batch, channel, in_height, in_width] or [batch, in_height, in_width, channel] + roi: Tuple of Float or Expr + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. + image_depth : integer Input image depth @@ -996,11 +1026,11 @@ def _resize_3d( layout: string, optional "NCHW", "NHWC", or "NCHWc". - coordinate_transformation_mode: string, optional + coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - Available options are "half_pixel", "align_corners" and "asymmetric". + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -1202,6 +1232,12 @@ def resize3d( [batch, channel, in_depth, in_height, in_width] or [batch, in_depth, in_height, in_width, channel] + roi: Tuple of Float or Expr + The region of interest for cropping the input image. Expected to be of + size 2 * ndim(data), and format [start_1, start_2, ..., start_x, + end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode + is tf_crop_and_resize. + size: Tuple Output resolution scale to @@ -1211,11 +1247,11 @@ def resize3d( method: string, optional method of interpolation ("nearest", "linear", "bicubic") - coordinate_transformation_mode: string, optional + coordinate_transformation_mode : string, optional Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. - Refer to the ONNX Resize operator specification for details. - Available options are "half_pixel", "align_corners" and "asymmetric". + [half_pixel, align_corners, asymmetric, pytorch_half_pixel, + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: Method for rounding coordinate locations diff --git a/tests/python/frontend/onnx/test_forward.py b/tests/python/frontend/onnx/test_forward.py index e9298eeb9cc7..0531bfcc9c46 100644 --- a/tests/python/frontend/onnx/test_forward.py +++ b/tests/python/frontend/onnx/test_forward.py @@ -4958,7 +4958,6 @@ def verify_eyelike(indata): "test_reduce_sum_keepdims_random", "test_reduce_sum_negative_axes_keepdims_example", "test_reduce_sum_negative_axes_keepdims_random", - # "test_resize_tf_crop_and_resize", "test_rnn_seq_length", "test_round", "test_scan9_sum", From 19b31dc2d8b09a536304b1e9e5430122abdf09c9 Mon Sep 17 00:00:00 2001 From: Matthew Brookhart Date: Mon, 15 Nov 2021 10:24:40 -0700 Subject: [PATCH 5/6] fix docstring indentation --- python/tvm/relay/op/image/image.py | 6 +++--- python/tvm/topi/image/resize.py | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/python/tvm/relay/op/image/image.py b/python/tvm/relay/op/image/image.py index 53ef95751c1c..047be0c2ee28 100644 --- a/python/tvm/relay/op/image/image.py +++ b/python/tvm/relay/op/image/image.py @@ -68,7 +68,7 @@ def resize1d( to the coordinate in the original tensor. Defintions can be found in topi/image/resize.py. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -164,7 +164,7 @@ def resize2d( to the coordinate in the original tensor. Defintions can be found in topi/image/resize.py. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -274,7 +274,7 @@ def resize3d( to the coordinate in the original tensor. Defintions can be found in topi/image/resize.py. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method diff --git a/python/tvm/topi/image/resize.py b/python/tvm/topi/image/resize.py index 6a5549ecc783..681de2f35b0a 100644 --- a/python/tvm/topi/image/resize.py +++ b/python/tvm/topi/image/resize.py @@ -251,7 +251,7 @@ def _resize_1d( Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -424,7 +424,7 @@ def resize1d( Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: Method for rounding coordinate locations @@ -570,7 +570,7 @@ def _resize_2d( Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -779,7 +779,7 @@ def resize2d( Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: Method for rounding coordinate locations @@ -1030,7 +1030,7 @@ def _resize_3d( Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: string, optional indicates how to find the "nearest" pixel in nearest_neighbor method @@ -1251,7 +1251,7 @@ def resize3d( Describes how to transform the coordinate in the resized tensor to the coordinate in the original tensor. [half_pixel, align_corners, asymmetric, pytorch_half_pixel, - tf_half_pixel_for_nn, and tf_crop_and_resize]. + tf_half_pixel_for_nn, and tf_crop_and_resize]. rounding_method: Method for rounding coordinate locations From 3dd483dae91df4e0c1955234b1bc61c4567153ac Mon Sep 17 00:00:00 2001 From: Matthew Brookhart Date: Mon, 15 Nov 2021 15:53:20 -0700 Subject: [PATCH 6/6] remove N anc C from resize roi --- python/tvm/relay/frontend/onnx.py | 17 ++++++-- python/tvm/relay/op/image/image.py | 21 ++++------ python/tvm/topi/image/resize.py | 46 +++++++++------------ python/tvm/topi/nn/upsampling.py | 4 +- src/relay/op/image/resize.cc | 6 +-- tests/python/topi/python/test_topi_image.py | 4 +- 6 files changed, 49 insertions(+), 49 deletions(-) diff --git a/python/tvm/relay/frontend/onnx.py b/python/tvm/relay/frontend/onnx.py index 8b3582a8fe65..0dc08d542b0b 100644 --- a/python/tvm/relay/frontend/onnx.py +++ b/python/tvm/relay/frontend/onnx.py @@ -2685,10 +2685,21 @@ def v11_13_common(cls, inputs, size, attr, params): exclude = attr.get("exclude_outside", 0) extrapolation_value = attr.get("extrapolation_value", 0.0) - out_size = fold_constant(_op.strided_slice(size, [2], [4])) + if roi is not None: + roi = fold_constant( + _op.concatenate( + [ + _op.strided_slice(roi, [2], [ndims]), + _op.strided_slice(roi, [ndims + 2], [2 * ndims]), + ], + axis=0, + ) + ) + + out_size = fold_constant(_op.strided_slice(size, [2], [ndims])) + out = None if ndims == 3: - out_size = fold_constant(_op.strided_slice(size, [2], [3])) out = _op.image.resize1d( inputs[0], out_size, @@ -2702,7 +2713,6 @@ def v11_13_common(cls, inputs, size, attr, params): extrapolation_value, ) elif ndims == 4: - out_size = fold_constant(_op.strided_slice(size, [2], [4])) out = _op.image.resize2d( inputs[0], out_size, @@ -2716,7 +2726,6 @@ def v11_13_common(cls, inputs, size, attr, params): extrapolation_value, ) elif ndims == 5: - out_size = fold_constant(_op.strided_slice(size, [2], [5])) out = _op.image.resize3d( inputs[0], out_size, diff --git a/python/tvm/relay/op/image/image.py b/python/tvm/relay/op/image/image.py index 047be0c2ee28..30f8a9c21118 100644 --- a/python/tvm/relay/op/image/image.py +++ b/python/tvm/relay/op/image/image.py @@ -53,9 +53,8 @@ def resize1d( roi: Tuple of Float or Expr, optional The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 2, and format [start_w, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. layout : str, optional Layout of the input. @@ -92,7 +91,7 @@ def resize1d( The resized result. """ if roi is None: - roi = [0.0] * 6 + roi = [0.0] * 2 if isinstance(size, Constant): size = list(size.data.numpy().astype("int32")) if isinstance(roi, Constant): @@ -149,9 +148,8 @@ def resize2d( roi: Tuple of Float or Expr, optional The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 4, and format [start_h, start_w, end_h, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. layout : str, optional Layout of the input. @@ -188,7 +186,7 @@ def resize2d( The resized result. """ if roi is None: - roi = [0.0] * 8 + roi = [0.0] * 4 if isinstance(size, Constant): size = list(size.data.numpy().astype("int32")) if isinstance(roi, Constant): @@ -259,9 +257,8 @@ def resize3d( roi: Tuple of Float or Expr, optional The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 6, and format [start_d, start_h, start_w, end_d, end_h, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. layout : str, optional Layout of the input. @@ -298,7 +295,7 @@ def resize3d( The resized result. """ if roi is None: - roi = [0.0] * 10 + roi = [0.0] * 6 if isinstance(size, Constant): size = list(size.data.numpy().astype("int32")) if isinstance(roi, Constant): diff --git a/python/tvm/topi/image/resize.py b/python/tvm/topi/image/resize.py index 681de2f35b0a..4fc68453a275 100644 --- a/python/tvm/topi/image/resize.py +++ b/python/tvm/topi/image/resize.py @@ -220,9 +220,8 @@ def _resize_1d( roi: Tuple of Float or Expr The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 2, and format [start_w, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. image_width : integer Input image width @@ -289,8 +288,8 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): image_width, target_width, coordinate_transformation_mode, - roi[2], - roi[5], + roi[0], + roi[1], ) if method == "nearest_neighbor": @@ -401,9 +400,8 @@ def resize1d( roi: Tuple of Float or Expr The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 2, and format [start_w, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. size: Tuple Output resolution scale to @@ -533,9 +531,8 @@ def _resize_2d( roi: Tuple of Float or Expr The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 4, and format [start_h, start_w, end_h, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. image_height : integer Input image height @@ -612,9 +609,9 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): in_y = y1 * (image_height - 1) + h_scale * y in_x = x1 * (image_width - 1) + w_scale * x else: - in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[3], roi[7]) + in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[1], roi[3]) in_y = get_inx( - y, image_height, target_height, coordinate_transformation_mode, roi[2], roi[6] + y, image_height, target_height, coordinate_transformation_mode, roi[0], roi[2] ) if method == "nearest_neighbor": @@ -756,9 +753,8 @@ def resize2d( roi: Tuple of Float or Expr The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 4, and format [start_h, start_w, end_h, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. size: Tuple Output resolution scale to @@ -934,7 +930,7 @@ def compute_func(*indices): return _resize_2d( indices, data, - [0.0] * 8, + [0.0] * 4, image_h, image_w, target_h, @@ -987,9 +983,8 @@ def _resize_3d( roi: Tuple of Float or Expr The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 6, and format [start_d, start_h, start_w, end_d, end_h, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. image_depth : integer Input image depth @@ -1063,9 +1058,9 @@ def _cast_output(value, data_dtype="float32", out_dtype=None): if boxes is not None: # TODO(mbrookhart): Find an example of this raise NotImplementedError("resize1d with image boxes not yet implemented") - in_z = get_inx(z, image_depth, target_depth, coordinate_transformation_mode, roi[4], roi[9]) - in_y = get_inx(y, image_height, target_height, coordinate_transformation_mode, roi[3], roi[8]) - in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[2], roi[7]) + in_z = get_inx(z, image_depth, target_depth, coordinate_transformation_mode, roi[2], roi[5]) + in_y = get_inx(y, image_height, target_height, coordinate_transformation_mode, roi[1], roi[4]) + in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[0], roi[3]) if method == "nearest_neighbor": if rounding_method == "": @@ -1234,9 +1229,8 @@ def resize3d( roi: Tuple of Float or Expr The region of interest for cropping the input image. Expected to be of - size 2 * ndim(data), and format [start_1, start_2, ..., start_x, - end_1, end_2, ..., end_x]. Only used if coordinate_transformation_mode - is tf_crop_and_resize. + size 6, and format [start_d, start_h, start_w, end_d, end_h, end_w]. + Only used if coordinate_transformation_mode is tf_crop_and_resize. size: Tuple Output resolution scale to diff --git a/python/tvm/topi/nn/upsampling.py b/python/tvm/topi/nn/upsampling.py index efd054aa593e..e9c810cd5e0a 100644 --- a/python/tvm/topi/nn/upsampling.py +++ b/python/tvm/topi/nn/upsampling.py @@ -96,7 +96,7 @@ def upsampling( method = method[2:] return topi.image.resize2d( data, - [0.0] * 8, + [0.0] * 4, reshape_size, layout=layout, method=method, @@ -195,7 +195,7 @@ def upsampling3d( method = method[3:] return topi.image.resize3d( data, - [0.0] * 10, + [0.0] * 6, resize_shape, layout=layout, method=method, diff --git a/src/relay/op/image/resize.cc b/src/relay/op/image/resize.cc index 7679550fc7e5..ca05a4bdce43 100644 --- a/src/relay/op/image/resize.cc +++ b/src/relay/op/image/resize.cc @@ -69,7 +69,7 @@ bool Resize1DRel(const Array& types, int num_inputs, const Attrs& attrs, const Resize1DAttrs* param = attrs.as(); ICHECK(param != nullptr); ICHECK(param->size.size() == 1); - ICHECK(param->roi.size() == 6); + ICHECK(param->roi.size() == 2); const Layout in_layout(param->layout); auto layout_converter = tir::BijectiveLayout(in_layout, kNCW); ICHECK(layout_converter.defined()) @@ -147,7 +147,7 @@ bool Resize2DRel(const Array& types, int num_inputs, const Attrs& attrs, const Resize2DAttrs* param = attrs.as(); ICHECK(param != nullptr); ICHECK(param->size.size() == 2); - ICHECK(param->roi.size() == 8); + ICHECK(param->roi.size() == 4); const Layout in_layout(param->layout); auto layout_converter = tir::BijectiveLayout(in_layout, kNCHW); ICHECK(layout_converter.defined()) @@ -226,7 +226,7 @@ bool Resize3DRel(const Array& types, int num_inputs, const Attrs& attrs, const Resize3DAttrs* param = attrs.as(); ICHECK(param != nullptr); ICHECK(param->size.size() == 3); - ICHECK(param->roi.size() == 10); + ICHECK(param->roi.size() == 6); const Layout in_layout(param->layout); auto layout_converter = tir::BijectiveLayout(in_layout, kNCDHW); ICHECK(layout_converter.defined()) diff --git a/tests/python/topi/python/test_topi_image.py b/tests/python/topi/python/test_topi_image.py index aef723a6d6c6..062cf7964ecb 100644 --- a/tests/python/topi/python/test_topi_image.py +++ b/tests/python/topi/python/test_topi_image.py @@ -49,7 +49,7 @@ def verify_resize2d( raise NotImplementedError("Layout not supported {} ".format(layout)) B = topi.image.resize2d( A, - [0.0] * 8, + [0.0] * 4, (out_height, out_width), layout=layout, coordinate_transformation_mode=coord_trans, @@ -128,7 +128,7 @@ def verify_resize3d( B = topi.image.resize3d( A, - [0.0] * 10, + [0.0] * 6, (out_depth, out_height, out_width), layout=layout, coordinate_transformation_mode=coordinate_transformation_mode,