在Python中旋转3D物体导致物体出现孔洞

0 投票
1 回答
43 浏览
提问于 2025-04-14 15:56

我有一个3D的物体,假设是一个长方体,我想让它旋转。为了简单起见,我们就假设它只围绕x轴旋转。所以我使用对应的旋转矩阵R,然后把它和坐标向量v相乘,得到新的坐标向量v'v'=R*v。为了可视化这个过程,我使用了mayavi这个工具。

虽然旋转是有效的,但它有一个让人烦恼的副作用:旋转后的长方体里面有些值缺失了。在下面的快照中,你可以看到,蓝色的长方体就是旋转后的物体,它里面有一些“洞”。

原始长方体和旋转后长方体的快照

我现在的问题是,我在旋转时做错了什么?

这里是对应的python代码:

# import standard modules
import numpy as np

# import modules for 3D visualization of data
from mayavi import mlab

def plot_simple( data2plot ):

    # define contour levels
    contLevels  = np.linspace(0, np.amax(data2plot), 5)[1:].tolist()

    # create figure with white background and black axes and labels
    fig1    = mlab.figure( bgcolor=(1,1,1), fgcolor=(0,0,0), size=(800,600) )

    # make the contour plot
    cont_plt    = mlab.contour3d( data2plot, contours=contLevels,
                                  transparent=True, opacity=.4,
                                  figure=fig1 )

    # create axes instance to modify some of its properties
    ax1 = mlab.axes( nb_labels=4, extent=[1, data2plot.shape[0],
                                          1, data2plot.shape[1],
                                          1, data2plot.shape[2] ] )
    mlab.outline(ax1)
    ax1.axes.label_format   = '%.0f'
    ax1.axes.x_label    = 'x'
    ax1.axes.y_label    = 'y'
    ax1.axes.z_label    = 'z'

    # set initial viewing angle
    mlab.view( azimuth=290, elevation=80 )

    mlab.show()


def Rx(alpha):
    # rotation matrix for rotation around x-axis
    return np.matrix([[ 1, 0            , 0             ],
                      [ 0, np.cos(alpha), -np.sin(alpha)],
                      [ 0, np.sin(alpha), np.cos(alpha) ]])


def make_rotated_cube( Nx=100, Ny=70, Nz=40 ):
    arr = np.zeros( [Nx, Ny, Nz] )

    # define center of cuboid
    xc  = Nx/2
    yc  = Ny/2
    zc  = Nz/2

    # define width of cuboid in each direction
    dx  = Nx/4
    dy  = Ny/4
    dz  = Nz/4

    # rotation angle in degrees
    alpha   = 20
    alpha   = np.radians(alpha)

    # loop through arr and define cuboid and rotated cuboid
    # note that this is a very inefficient way to define a cuboid
    # (the actual thing to rotate is different, this is just to make it simple)
    for ii in range(Nx):
        for jj in range(Ny):
            for kk in range(Nz):
                # check if coordinate is inside original cuboid
                if (    (ii > (xc-dx/2) and ii < (xc+dx/2))
                    and (jj > (yc-dy/2) and jj < (yc+dy/2))
                    and (kk > (zc-dz/2) and kk < (zc+dz/2)) ):
                    # set density of original cuboid
                    arr[ii,jj,kk]   = 5.

                    # apply rotation
                    new_coords  = Rx(alpha)*np.array([[ii],[jj],[kk]])
                    # set density of rotated cuboid to different value
                    arr[ round(new_coords[0,0]),
                         round(new_coords[1,0]),
                         round(new_coords[2,0]) ] = 2

    return arr


def main():
    cubes   = make_rotated_cube()
    plot_simple(cubes)


if __name__ == '__main__':
    main()

1 个回答

0

感谢@Julien和他的评论,我试了一下扭曲的方法。从下面的图可以看到,之前的孔确实消失了!不过,现在可以明显看出边缘有点难看,我觉得这可能是因为网格的分辨率不够好(因为用这种方法我没有“丢失”任何像素,之前是有的)。

通过一系列扭曲产生的旋转立方体

我只是修改了for循环中的代码(在# apply rotation这条评论之后):

# translations
skew_1  = np.tan(alpha/2)
skew_2  = -np.sin(alpha)
skew_3  = skew_1
# calculating new coords
kk_new   = kk + round(skew_1*jj)
jj_new   = jj + round(skew_2*kk_new)
kk_new   = kk_new + round(skew_1*jj_new)
arr[ ii, jj_new, kk_new ] = 2

我还是希望能看到一个涉及插值的解决方案(不过这个扭曲的方法真的很不错)。

撰写回答