Python 如何将这个numpy旋转代码矢量化? 目标是:

Python 如何将这个numpy旋转代码矢量化? 目标是:,python,numpy,Python,Numpy,我想矢量化(或加速)这段代码。它围绕其中心点旋转3dnumpy模型(让x、y、z表示尺寸;然后我们要围绕z轴旋转)。np模型是“开”或“关”的二元体素 我打赌一些基本的矩阵运算可以做到这一点,比如取一层并将旋转矩阵应用于每个元素。唯一的问题是小数;既然cos(pi/6)==sqrt(3)/2,我应该在哪里拥有新的价值地 守则: 其中rotate()是: def rotate(arr, theta): ''' Rotates theta clockwise

我想矢量化(或加速)这段代码。它围绕其中心点旋转3d
numpy
模型(让x、y、z表示尺寸;然后我们要围绕z轴旋转)。np模型是“开”或“关”的二元体素

我打赌一些基本的矩阵运算可以做到这一点,比如取一层并将旋转矩阵应用于每个元素。唯一的问题是小数;既然
cos(pi/6)==sqrt(3)/2
,我应该在哪里拥有新的价值地

守则: 其中
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

下面是一些代码,供同样做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,
                        :
        ]

您的代码中有几个问题。首先,如果要将原始图像拟合到旋转的网格上,则需要更大的网格(通常)。或者,想象一个规则的网格,但对象的形状(矩形)被旋转,从而成为“菱形”。很明显,如果你想适应整个菱形,你需要一个更大的输出网格(数组)。另一方面,您在代码中说“
rotated.shape==arr.shape
,而不像
scipy.ndimage.rotate()
,它使大小膨胀”。如果是这样的话,也许您不想拟合整个对象?所以,也许可以这样做:
rotated=np.zeros(arr.shape)
。但一般来说,是的,一个必须有一个更大的网格,以适应整个输入图像旋转后

另一个问题是正在进行的角度转换:

if theta == int(theta):
    theta *= pi / 180
theta = -theta
为什么???当我想将图像旋转1弧度时会发生什么?还是2弧度?我被禁止使用整数弧度吗?我认为您试图在这个函数中做的太多了,因此使用它会非常混乱。只需要调用方将角度转换为弧度。或者,如果输入
theta
始终以度为单位,则可以在该函数中执行此操作。或者,您可以添加另一个名为的参数,例如,
单位
,调用者可以将其设置为
弧度
。不要试图根据输入的“整数度”来猜测它

现在,让我们稍微重写一下您的代码:

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]
rotated=np.zero_like(arr)#而不是np.zero(arr.shape)
y_mid=arr.shape[0]//2
x_mid=arr.shape[1]//2

#val=0我只是想到了一些东西;如果我在布尔型上尝试
scipy.ndimage.rotate()
,可能它们不会像在数字上那样添加。确认…@AGNGazer我不是100%确定结束一个问题的标准,但我要的是代码。我不是问“如何在极坐标和笛卡尔坐标之间转换?”这是一个数学问题。我的问题在本质上是非常计算性和实用性的,你必须理解(在一般情况下)没有办法旋转一堆像素,旋转的坐标是整数。因此,您需要将原始图像
I(I,j)
重新采样到新的网格
I(I',j')
。所以,是的,如果你的问题没有问题-你可以四舍五入到最近的网格节点。如果你需要做得更好-使用插值。我误解了你的问题。
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]
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