Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion python/tvm/relax/frontend/onnx/onnx_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3113,6 +3113,7 @@ def _emit_resize_topi_dynamic_roi(
cubic_coeff_a: float,
exclude_outside: int,
extrapolation_value: float,
scales: list | None,
) -> relax.Expr:
"""Lower Resize with runtime ROI via TOPI, which supports Expr ROI."""
if rank == 3:
Expand All @@ -3129,6 +3130,7 @@ def resize1d_dyn(d, r, s0):
cubic_coeff_a,
exclude_outside,
extrapolation_value,
scales=scales,
)

return bb.emit_te(resize1d_dyn, data, roi_spatial_vec, sizes_spatial[0])
Expand All @@ -3147,12 +3149,14 @@ def resize2d_dyn(d, r, s0, s1):
bicubic_alpha=cubic_coeff_a,
bicubic_exclude=exclude_outside,
extrapolation_value=extrapolation_value,
scales=scales,
)

return bb.emit_te(resize2d_dyn, data, roi_spatial_vec, sizes_spatial[0], sizes_spatial[1])

def resize3d_dyn(d, r, s0, s1, s2):
# r is ONNX order (D,H,W) x2; TOPI expects (W,H,D) x2.
# NOTE: scales order must stay ONNX (D,H,W), unlike r which is reordered
return topi.image.resize3d(
d,
(r[2], r[1], r[0], r[5], r[4], r[3]),
Expand All @@ -3164,6 +3168,7 @@ def resize3d_dyn(d, r, s0, s1, s2):
bicubic_alpha=cubic_coeff_a,
bicubic_exclude=exclude_outside,
extrapolation_value=extrapolation_value,
scales=scales,
)

return bb.emit_te(
Expand Down Expand Up @@ -3230,14 +3235,18 @@ def _impl_v18(cls, bb, inputs, attr, params):

use_dynamic_roi = roi_dynamic_vec is not None

# Convert scales to sizes if needed.
# Convert scales to sizes if needed, preserving the original spatial scales so
# the coordinate transformation uses the exact ONNX scale value rather than the
# lossy ratio derived from floor(input * scale) / input.
original_spatial_scales = None
if scales is not None:
if isinstance(scales, relax.Constant):
scales = scales.data.numpy()
elif isinstance(scales, relax.expr.ShapeExpr):
scales = [int(val.value) for val in scales.values]
else:
raise ValueError(f"Type {type(scales)} for scale is currently unsupported.")
original_spatial_scales = [float(s) for s in scales[2:]]
sizes = []

for i, dim in enumerate(x.struct_info.shape):
Expand All @@ -3264,6 +3273,7 @@ def _impl_v18(cls, bb, inputs, attr, params):
cubic_coeff_a,
exclude_outside,
extrapolation_value,
original_spatial_scales,
)

if ndims == 3:
Expand All @@ -3279,8 +3289,24 @@ def _impl_v18(cls, bb, inputs, attr, params):
cubic_coeff_a,
exclude_outside,
extrapolation_value,
scales=original_spatial_scales,
)
elif ndims == 4:
if original_spatial_scales is not None:
return bb.emit_te(
topi.image.resize2d,
x,
roi_static,
sizes,
"NCHW",
topi_mode,
coord_mode,
rounding_method,
cubic_coeff_a,
exclude_outside,
extrapolation_value,
scales=original_spatial_scales,
)
return relax.op.image.resize2d(
x,
size=relax.ShapeExpr(sizes),
Expand All @@ -3295,6 +3321,21 @@ def _impl_v18(cls, bb, inputs, attr, params):
)
else: # ndims == 5
roi3d = _topi_resize3d_roi_from_onnx_ncdhw_spatial(roi_static)
if original_spatial_scales is not None:
return bb.emit_te(
topi.image.resize3d,
x,
roi3d,
sizes,
"NCDHW",
topi_mode,
coord_mode,
rounding_method,
cubic_coeff_a,
exclude_outside,
extrapolation_value,
scales=original_spatial_scales,
)
return relax.op.image.resize3d(
x,
size=relax.ShapeExpr(sizes),
Expand Down
78 changes: 71 additions & 7 deletions python/tvm/topi/image/resize.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,13 @@ def get_inx(
start_x=0,
end_x=-1,
use_int_div=False,
scale_x_override=None,
):
"""Infer input x from output x with various coordinate transformation methods"""
scale_x = te.div(image_width.astype("float"), target_width.astype("float"))
if scale_x_override is not None:
scale_x = scale_x_override
else:
scale_x = te.div(image_width.astype("float"), target_width.astype("float"))
if coordinate_transformation_mode == "half_pixel":
in_x = (x + 0.5) * scale_x - 0.5
elif coordinate_transformation_mode == "align_corners":
Expand Down Expand Up @@ -237,6 +241,7 @@ def _resize_1d(
alpha=-0.5,
exclude_outside=0,
out_dtype=None,
scale_x=None,
):
"""Perform resize operation on the data with selected method and options.

Expand Down Expand Up @@ -315,7 +320,15 @@ 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_x = get_inx(x, image_width, target_width, coordinate_transformation_mode, roi[0], roi[1])
in_x = get_inx(
x,
image_width,
target_width,
coordinate_transformation_mode,
roi[0],
roi[1],
scale_x_override=scale_x,
)

if method == "nearest_neighbor":
if rounding_method == "":
Expand Down Expand Up @@ -383,6 +396,7 @@ def resize1d(
extrapolation_value=0.0,
out_dtype=None,
output_shape=None,
scales=None,
):
"""Perform resize operation on the data.

Expand Down Expand Up @@ -472,6 +486,8 @@ def resize1d(
if isinstance(size[i], int):
size[i] = tvm.tirx.IntImm("int32", size[i])

scale_x = (1.0 / scales[0]) if scales is not None else None

def compute_func(*indices):
return _resize_1d(
indices,
Expand All @@ -487,6 +503,7 @@ def compute_func(*indices):
exclude_outside=bicubic_exclude,
extrapolation_value=extrapolation_value,
out_dtype=out_dtype,
scale_x=scale_x,
)

return te.compute(output_shape, compute_func, name="resize", tag=tag.INJECTIVE)
Expand All @@ -510,6 +527,8 @@ def _resize_2d(
alpha=-0.5,
exclude_outside=0,
out_dtype=None,
scale_h=None,
scale_w=None,
):
"""Perform resize operation on the data with selected method and options.

Expand Down Expand Up @@ -593,8 +612,10 @@ def _cast_output(value, data_dtype="float32", out_dtype=None):
width_use_int_div = False
if method == "nearest_neighbor" and coordinate_transformation_mode == "asymmetric":
if rounding_method == "floor" or rounding_method == "":
height_use_int_div = can_convert_multiply_to_intdiv(image_height, target_height)
width_use_int_div = can_convert_multiply_to_intdiv(image_width, target_width)
if scale_h is None:
height_use_int_div = can_convert_multiply_to_intdiv(image_height, target_height)
if scale_w is None:
width_use_int_div = can_convert_multiply_to_intdiv(image_width, target_width)

n, c, y, x, cc, inum, ic = get_2d_indices(indices, layout)
box_idx = box_indices(n) if box_indices is not None else n
Expand All @@ -618,6 +639,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None):
roi[1],
roi[3],
width_use_int_div,
scale_x_override=scale_w,
)
in_y = get_inx(
y,
Expand All @@ -627,6 +649,7 @@ def _cast_output(value, data_dtype="float32", out_dtype=None):
roi[0],
roi[2],
height_use_int_div,
scale_x_override=scale_h,
)

if method == "nearest_neighbor":
Expand Down Expand Up @@ -756,6 +779,7 @@ def resize2d(
extrapolation_value=0.0,
out_dtype=None,
output_shape=None,
scales=None,
):
"""Perform resize operation on the data.

Expand Down Expand Up @@ -839,6 +863,9 @@ def resize2d(
if isinstance(size[i], int):
size[i] = tvm.tirx.IntImm("int32", size[i])

scale_h = (1.0 / scales[0]) if scales is not None else None
scale_w = (1.0 / scales[1]) if scales is not None else None

def compute_func(*indices):
return _resize_2d(
indices,
Expand All @@ -856,6 +883,8 @@ def compute_func(*indices):
exclude_outside=bicubic_exclude,
extrapolation_value=extrapolation_value,
out_dtype=out_dtype,
scale_h=scale_h,
scale_w=scale_w,
)

return te.compute(output_shape, compute_func, name="resize", tag=tag.INJECTIVE)
Expand Down Expand Up @@ -976,6 +1005,9 @@ def _resize_3d(
alpha=-0.5,
exclude_outside=0,
out_dtype=None,
scale_d=None,
scale_h=None,
scale_w=None,
):
"""Perform resize operation on the data with selected method and options.

Expand Down Expand Up @@ -1066,9 +1098,33 @@ 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[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])
in_z = get_inx(
z,
image_depth,
target_depth,
coordinate_transformation_mode,
roi[2],
roi[5],
scale_x_override=scale_d,
)
in_y = get_inx(
y,
image_height,
target_height,
coordinate_transformation_mode,
roi[1],
roi[4],
scale_x_override=scale_h,
)
in_x = get_inx(
x,
image_width,
target_width,
coordinate_transformation_mode,
roi[0],
roi[3],
scale_x_override=scale_w,
)

if method == "nearest_neighbor":
if rounding_method == "":
Expand Down Expand Up @@ -1225,6 +1281,7 @@ def resize3d(
extrapolation_value=0.0,
out_dtype=None,
output_shape=None,
scales=None,
):
"""Perform resize operation on the data.

Expand Down Expand Up @@ -1302,6 +1359,10 @@ def resize3d(
if isinstance(size[i], int):
size[i] = tvm.tirx.IntImm("int32", size[i])

scale_d = (1.0 / scales[0]) if scales is not None else None
scale_h = (1.0 / scales[1]) if scales is not None else None
scale_w = (1.0 / scales[2]) if scales is not None else None

def compute_func(*indices):
return _resize_3d(
indices,
Expand All @@ -1321,6 +1382,9 @@ def compute_func(*indices):
exclude_outside=bicubic_exclude,
extrapolation_value=extrapolation_value,
out_dtype=out_dtype,
scale_d=scale_d,
scale_h=scale_h,
scale_w=scale_w,
)

return te.compute(output_shape, compute_func, name="resize", tag=tag.INJECTIVE)
Loading
Loading