我想向量化(或加快)此代码。它绕其中心点旋转3d numpy
模型(用x,y,z表示尺寸;然后我们要绕z轴旋转)。 np模型是“开”或“关”的二进制体素
我敢打赌,一些基本的矩阵运算可以做到这一点,例如取一层并将旋转矩阵应用于每个元素。唯一的问题是小数;自cos(pi / 6) == sqrt(3) / 2
起,我应该在哪里拥有新的价值之地?
def rotate_model(m, theta):
'''
theta in degrees
'''
n =np.zeros(m.shape)
for i,layer in enumerate(m):
rotated = rotate(layer,theta)
n[i] = rotated
return n
其中rotate()
是:
def rotate(arr, theta):
'''
Rotates theta clockwise
rotated.shape == arr.shape, unlike scipy.ndimage.rotate(), which inflates size and also does some strange mixing
'''
if theta == int(theta):
theta *= pi / 180
theta = -theta
# theta=-theta b/c clockwise. Otherwise would default to counterclockwise
rotated =np.zeros(arr.shape)
#print rotated.shape[0], rotated.shape[1]
y_mid = arr.shape[0]//2
x_mid = arr.shape[1]//2
val = 0
for x_new in range(rotated.shape[1]):
for y_new in range(rotated.shape[0]):
x_centered = x_new - x_mid
y_centered = y_new - y_mid
x = x_centered*cos(theta) - y_centered*sin(theta)
y = x_centered*sin(theta) + y_centered*cos(theta)
x += x_mid
y += y_mid
x = int(round(x)); y = int(round(y)) # cast so range() picks it up
# lossy rotation
if x in range(arr.shape[1]) and y in range(arr.shape[0]):
val = arr[y,x]
rotated[y_new,x_new] = val
#print val
#print x,y
return rotated
答案 0 :(得分:1)
您的代码中有几个问题。首先,如果要将原始图像拟合到旋转的网格上,则需要一个更大的网格(通常)。或者,想象一个规则的网格,但是对象的形状-一个矩形-旋转了,因此变成了“菱形”。很明显,如果要适合整个菱形,则需要更大的输出网格(数组)。另一方面,您在代码“ rotated.shape == arr.shape
中说,与scipy.ndimage.rotate()
不同,后者会夸大大小” 。如果是这样,也许您想不适合整个对象?因此,也许可以这样做:rotated=np.zeros(arr.shape)
。但是,总的来说,是的,必须有一个较大的网格,以便在旋转输入图像后使其适合整个输入图像。
另一个问题是您正在执行的角度转换:
if theta == int(theta):
theta *= pi / 180
theta = -theta
为什么?我想将图像旋转1个弧度会怎样?还是2个弧度?我禁止使用整数弧度吗?我认为您正在尝试在此功能中做太多事情,因此使用它会非常令人困惑。只需要求呼叫者将角度转换为弧度即可。或者,如果输入theta
总是 度,则可以在此函数内执行此操作。或者,您可以添加另一个参数,例如units
,调用者可以将其设置为radians
或degrees
。不要试图根据输入的“整数”来猜测它!
现在,让我们重写一下代码:
rotated = np.zeros_like(arr) # instead of np.zero(arr.shape)
y_mid = arr.shape[0] // 2
x_mid = arr.shape[1] // 2
# val = 0 <- this is unnecessary
# pre-compute cos(theta) and sin(theta):
cs = cos(theta)
sn = sin(theta)
for x_new in range(rotated.shape[1]):
for y_new in range(rotated.shape[0]):
x = int(round((x_new - x_mid) * cs - (y_new - y_mid) * sn + x_mid)
y = int(round((x_new - x_mid) * sn - (y_new - y_mid) * cs + y_mid)
# just use comparisons, don't search through many values!
if 0 <= x < arr.shape[1] and 0 <= y < arr.shape[0]:
rotated[y_new, x_new] = arr[y, x]
因此,现在,我可以(更容易地)看到,对于 output 数组中的每个像素,它们都映射到 input 数组中的某个位置。是的,您可以对此向量化。
import numpy as np
def rotate(arr, theta, unit='rad'):
# deal with theta units:
if unit.startswith('deg'):
theta = np.deg2rad(theta)
# for convenience, store array size:
ny, nx = arr.shape
# generate arrays of indices and flatten them:
y_new, x_new = np.indices(arr.shape)
x_new = x_new.ravel()
y_new = y_new.ravel()
# compute center of the array:
x0 = nx // 2
y0 = ny // 2
# compute old coordinates
xc = x_new - x0
yc = y_new - y0
x = np.round(np.cos(theta) * xc - np.sin(theta) * yc + x0).astype(np.int)
y = np.round(np.sin(theta) * xc - np.cos(theta) * yc + y0).astype(np.int)
# main idea to deal with indices is to create a mask:
mask = (x >= 0) & (x < nx) & (y >= 0) & (y < ny)
# ... and then select only those coordinates (both in
# input and "new" coordinates) that satisfy the above condition:
x = x[mask]
y = y[mask]
x_new = x_new[mask]
y_new = y_new[mask]
# map input values to output pixels *only* for selected "good" pixels:
rotated = np.zeros_like(arr)
rotated[y_new, x_new] = arr[y, x]
return rotated
答案 1 :(得分:-1)
这里有一些代码也适合进行3D建模的任何人。它很好地解决了我的特定用例。仍在寻找如何在适当的平面内旋转。希望它对您也有帮助:
def rotate_model(m, theta):
'''
Redefines the prev 'rotate_model()' method
theta has to be in degrees
'''
rotated = scipy.ndimage.rotate(m, theta, axes=(1,2))
# have tried (1,0), (2,0), and now (1,2)
# ^ z is "up" and "2"
# scipy.ndimage.rotate() shrinks the model
# TODO: regrow it back
x_r = rotated.shape[1]
y_r = rotated.shape[0]
x_m = m.shape[1]
y_m = m.shape[0]
x_diff = abs(x_r - x_m)
y_diff = abs(y_r - y_m)
if x_diff%2==0 and y_diff%2==0:
return rotated[
x_diff//2 : x_r-x_diff//2,
y_diff//2 : y_r-y_diff//2,
:
]
elif x_diff%2==0 and y_diff%2==1:
# if this shift ends up turning the model to shit in a few iterations,
# change the following lines to include a flag that alternates cutting off the top and bottom bits of the array
return rotated[
x_diff//2 : x_r-x_diff//2,
y_diff//2+1 : y_r-y_diff//2,
:
]
elif x_diff%2==1 and y_diff%2==0:
return rotated[
x_diff//2+1 : x_r-x_diff//2,
y_diff//2 : y_r-y_diff//2,
:
]
else:
# x_diff%2==1 and y_diff%2==1:
return rotated[
x_diff//2+1 : x_r-x_diff//2,
y_diff//2+1 : y_r-y_diff//2,
:
]