Files
wehub-resource-sync 26446540fa
Lint / lint (push) Waiting to run
CI / MacOS (push) Waiting to run
CI / Windows (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:25 +08:00

308 lines
10 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=invalid-name, line-too-long, unused-variable, too-many-locals
# ruff: noqa: E741, F841, RUF005
"""Upsampling in python"""
import math
import numpy as np
from tvm.topi.utils import nchw_pack_layout
def get_inx(x, image_width, target_width, coordinate_transformation_mode):
"""Infer input x from output x with various coordinate transformation methods"""
scale = image_width / target_width
if coordinate_transformation_mode == "half_pixel":
in_x = (x + 0.5) * scale - 0.5
elif coordinate_transformation_mode == "align_corners":
in_x = (image_width - 1) / (target_width - 1) * x if target_width > 1 else 0
elif coordinate_transformation_mode == "asymmetric":
in_x = scale * x
else:
raise ValueError(
f"Unsupported coordinate_transformation_mode: {coordinate_transformation_mode}"
)
return in_x
def get_index(x, image_width, target_width, coordinate_transformation_mode, rounding_method=""):
"""get and round the nearest index for nearest_neighbor"""
in_x = get_inx(x, image_width, target_width, coordinate_transformation_mode)
effective_rounding_method = rounding_method
if not effective_rounding_method:
if coordinate_transformation_mode == "align_corners":
effective_rounding_method = "round"
else:
effective_rounding_method = "floor"
if effective_rounding_method == "floor":
out = math.floor(in_x)
elif effective_rounding_method == "round":
out = round(in_x)
elif effective_rounding_method == "round_prefer_floor":
out = math.ceil(in_x - 0.5)
elif effective_rounding_method == "round_prefer_ceil":
out = math.floor(in_x + 0.5)
elif effective_rounding_method == "ceil":
out = math.ceil(in_x)
else:
raise ValueError(f"Unknown rounding method: {rounding_method!r}")
out = max(min(out, image_width - 1), 0)
return int(out)
def resize3d_nearest(arr, scale, coordinate_transformation_mode, rounding_method=""):
"""Populate the array by scale factor"""
d, h, w = arr.shape
out_d, out_h, out_w = [round(i * s) for i, s in zip(arr.shape, scale)]
out = np.empty((out_d, out_h, out_w))
for z in range(out_d):
for y in range(out_h):
for x in range(out_w):
in_z = get_index(z, d, out_d, coordinate_transformation_mode, rounding_method)
in_y = get_index(y, h, out_h, coordinate_transformation_mode, rounding_method)
in_x = get_index(x, w, out_w, coordinate_transformation_mode, rounding_method)
out[z, y, x] = arr[in_z, in_y, in_x]
return out
def resize3d_linear(data_in, scale, coordinate_transformation_mode):
"""Trilinear 3d scaling using python"""
dtype = data_in.dtype
d, h, w = data_in.shape
new_d, new_h, new_w = [round(i * s) for i, s in zip(data_in.shape, scale)]
data_out = np.ones((new_d, new_h, new_w))
indexes = np.mgrid[0:2, 0:2, 0:2]
def _get_patch(zint, yint, xint):
# Get the surrounding values
indices = indexes.copy()
indices[0] = np.maximum(np.minimum(indexes[0] + zint, d - 1), 0)
indices[1] = np.maximum(np.minimum(indexes[1] + yint, h - 1), 0)
indices[2] = np.maximum(np.minimum(indexes[2] + xint, w - 1), 0)
p = data_in[indices[0], indices[1], indices[2]]
return p
for m in range(new_d):
for j in range(new_h):
for k in range(new_w):
in_z = get_inx(m, d, new_d, coordinate_transformation_mode)
in_y = get_inx(j, h, new_h, coordinate_transformation_mode)
in_x = get_inx(k, w, new_w, coordinate_transformation_mode)
zint = math.floor(in_z)
zfract = in_z - math.floor(in_z)
yint = math.floor(in_y)
yfract = in_y - math.floor(in_y)
xint = math.floor(in_x)
xfract = in_x - math.floor(in_x)
wz = np.array([1.0 - zfract, zfract], dtype=dtype)
wy = np.array([1.0 - yfract, yfract], dtype=dtype)
wx = np.array([1.0 - xfract, xfract], dtype=dtype)
p = _get_patch(zint, yint, xint)
l = np.sum(p * wx, axis=-1)
col = np.sum(l * wy, axis=-1)
data_out[m, j, k] = np.sum(col * wz)
return data_out
def resize3d_cubic(data_in, scale, coordinate_transformation_mode):
"""Tricubic 3d scaling using python"""
dtype = data_in.dtype
d, h, w = data_in.shape
new_d, new_h, new_w = [round(i * s) for i, s in zip(data_in.shape, scale)]
data_out = np.ones((new_d, new_h, new_w))
def _cubic_spline_weights(t, alpha=-0.5):
"""create cubic spline weights in 1D"""
t2 = t * t
t3 = t * t * t
w1 = alpha * (t3 - 2 * t2 + t)
w2 = (alpha + 2) * t3 - (3 + alpha) * t2 + 1
w3 = -(alpha + 2) * t3 + (3 + 2 * alpha) * t2 - alpha * t
w4 = -alpha * t3 + alpha * t2
return np.array([w1, w2, w3, w4])
indexes = np.mgrid[-1:3, -1:3, -1:3]
def _get_patch(zint, yint, xint):
# Get the surrounding values
indices = indexes.copy()
indices[0] = np.maximum(np.minimum(indexes[0] + zint, d - 1), 0)
indices[1] = np.maximum(np.minimum(indexes[1] + yint, h - 1), 0)
indices[2] = np.maximum(np.minimum(indexes[2] + xint, w - 1), 0)
p = data_in[indices[0], indices[1], indices[2]]
return p
for m in range(new_d):
for j in range(new_h):
for k in range(new_w):
in_z = get_inx(m, d, new_d, coordinate_transformation_mode)
in_y = get_inx(j, h, new_h, coordinate_transformation_mode)
in_x = get_inx(k, w, new_w, coordinate_transformation_mode)
zint = math.floor(in_z)
zfract = in_z - math.floor(in_z)
yint = math.floor(in_y)
yfract = in_y - math.floor(in_y)
xint = math.floor(in_x)
xfract = in_x - math.floor(in_x)
wz = _cubic_spline_weights(zfract)
wy = _cubic_spline_weights(yfract)
wx = _cubic_spline_weights(xfract)
p = _get_patch(zint, yint, xint)
l = np.sum(p * wx, axis=-1)
col = np.sum(l * wy, axis=-1)
data_out[m, j, k] = np.sum(col * wz)
return data_out
def resize3d_ncdhw(
data,
scale,
method="nearest_neighbor",
coordinate_transformation_mode="align_corners",
rounding_method="",
):
"""reference kernel for 3D image resizing"""
ishape = data.shape
oshape = (
ishape[0],
ishape[1],
round(ishape[2] * scale[0]),
round(ishape[3] * scale[1]),
round(ishape[4] * scale[2]),
)
output_np = np.zeros(oshape, dtype=data.dtype)
for b in range(oshape[0]):
for c in range(oshape[1]):
if method == "nearest_neighbor":
output_np[b, c, :, :, :] = resize3d_nearest(
data[b, c, :, :, :], scale, coordinate_transformation_mode, rounding_method
)
elif method == "linear":
output_np[b, c, :, :, :] = resize3d_linear(
data[b, c, :, :, :], scale, coordinate_transformation_mode
)
elif method == "cubic":
output_np[b, c, :, :, :] = resize3d_cubic(
data[b, c, :, :, :], scale, coordinate_transformation_mode
)
else:
raise ValueError("Unknown resize method", method)
return output_np
def resize1d_python(
data,
scale,
layout="NCW",
method="nearest_neighbor",
coordinate_transformation_mode="align_corners",
rounding_method="",
):
"""Python version of 3D scaling using nearest neighbour"""
if layout == "NWC":
data = data.transpose([0, 2, 1])
data = np.expand_dims(data, axis=[2, 3])
output_np = resize3d_ncdhw(
data, (1, 1) + scale, method, coordinate_transformation_mode, rounding_method
)
output_np = np.squeeze(output_np, axis=2)
output_np = np.squeeze(output_np, axis=2)
if layout == "NWC":
output_np = output_np.transpose([0, 2, 1])
return output_np
def resize2d_python(
data,
scale,
layout="NCHW",
method="nearest_neighbor",
coordinate_transformation_mode="align_corners",
rounding_method="",
):
"""Python version of scaling using nearest neighbour"""
if layout == "NHWC":
data = data.transpose([0, 3, 1, 2])
elif nchw_pack_layout(layout):
ishape = data.shape
transposed = data.transpose([0, 4, 1, 5, 2, 3])
tshape = transposed.shape
data = transposed.reshape(
tshape[0] * tshape[1], tshape[2] * tshape[3], tshape[4], tshape[5]
)
data = np.expand_dims(data, axis=2)
output_np = resize3d_ncdhw(
data, (1,) + scale, method, coordinate_transformation_mode, rounding_method
)
output_np = np.squeeze(output_np, axis=2)
if layout == "NHWC":
output_np = output_np.transpose([0, 2, 3, 1])
elif nchw_pack_layout(layout):
output_np = output_np.reshape(tshape[0:4] + output_np.shape[2:])
output_np = output_np.transpose([0, 2, 4, 5, 1, 3])
return output_np
def resize3d_python(
data,
scale,
layout="NCDHW",
method="nearest_neighbor",
coordinate_transformation_mode="align_corners",
rounding_method="",
):
"""Python version of 3D scaling using nearest neighbour"""
if layout == "NDHWC":
data = data.transpose([0, 4, 1, 2, 3])
output_np = resize3d_ncdhw(data, scale, method, coordinate_transformation_mode, rounding_method)
if layout == "NDHWC":
output_np = output_np.transpose([0, 2, 3, 4, 1])
return output_np