如何在3D图中显示真彩色2D RGB纹理?

9

我正在尝试制作一个由RGB堆叠中的一系列2D平面组成的3D图,就像这样:

enter image description here

我知道可以使用mpl_toolkits.mplot3d来实现这个功能,只需将每个像素的x、y、z坐标和RGB(A)颜色传递给plot_surface即可:

import numpy as np
from matplotlib import pyplot as pp
from mpl_toolkits.mplot3d.axes3d import Axes3D

def plot_stack_slices(rgbstack, scale=(1., 1., 1.), z_interval=10.):

    fig, ax = pp.subplots(1,1,subplot_kw={'projection':'3d'})
    ax.invert_zaxis()
    ax.hold(True)

    sx, sy, sz = scale
    nz, ny, nx, nc = rgbstack.shape

    stack_xyz = np.mgrid[:nx*sx:nx*1j, :ny*sy:ny*1j, :nz*sz:nz*1j]

    slices = rgbstack[::-z_interval]
    slice_xyz = np.rollaxis(stack_xyz, 3, 0)[::-z_interval]

    surflist = []

    for (img,xyz) in zip(slices, slice_xyz):
        x, y, z = xyz
        s = ax.plot_surface(x, y, z, facecolors=img**0.75, 
            rstride=50, cstride=50)
        surflist.append(s)

    return fig, ax, surflist

不幸的是,如果我设置rstride=1, cstride=1以显示完整分辨率的纹理,这会变得极其缓慢。

我也知道Mayavi可以轻松处理显示多个2D纹理的完整分辨率:

from mayavi import mlab

def plot_stack_slices2(stack, scale=(1., 1., 20.), z_interval=10.):

    mfig = mlab.figure(bgcolor=(1,)*3)

    sx, sy, sz = scale
    nz, ny, nx = stack.shape

    slices = stack[::-z_interval]
    slice_z = np.linspace(0,nz*sz,nz)[::z_interval]

    surflist = []

    for (img,z) in zip(slices, slice_z):
        im = mlab.imshow(img.T, colormap='gray', figure=mfig)
        im.actor.scale = [sx,sy,sz]
        im.actor.position = [0, 0, z]
        surflist.append(z)


    return fig, surflist

然而,现在的问题是似乎没有任何方法可以使用Mayavi显示真彩色RGB纹理 - 根据文档我只能指定单个(R, G, B)元组或预定义的颜色映射。

有人知道在3D图中显示真彩色2D RGB纹理的更好方法吗?

如果有足够的时间,我可能可以弄清楚如何在Vtk甚至纯OpenGL中实现这一点,但我真的希望有现有的库可以完成这项工作。


1
请尝试使用vivis volshow - cgohlke
@cgohlke 哇,这看起来确实非常有前途! - ali_m
你尝试过使用.pcolor绘制它们吗?请参见:http://stackoverflow.com/questions/14355876/matplotlib-plot-3d-data-with-alpha 不过你需要很大的内存! - Rutger Kassies
@RutgerKassies,使用pcolor()是否可以为每个像素指定RGB(A)值?我尝试将一个由1组成的数组作为C传递,并将RGB值的元组作为facecolors=传递,但没有成功。即使绘制单个平面也非常慢,因此我认为OpenGL后端是正确的选择,就像visvis一样。 - ali_m
2个回答

6

感谢aestrivex提供使用Mayavi/VTK的有效解决方案 - 这是有用的信息,将来我可能需要做更复杂的事情。

最终,我选择采用cgohlke建议的visvis,结果证明这要简单得多:

import visvis as vv
vv.use('wx')

import numpy as np
from matplotlib.image import imread
from matplotlib.cbook import get_sample_data

imgdata = imread(get_sample_data('lena.png'))

nr, nc = imgdata.shape[:2]
x,y = np.mgrid[:nr, :nc]
z = np.ones((nr, nc))

for ii in xrange(5):
    vv.functions.surf(x, y, z*ii*100, imgdata, aa=3)

enter image description here


4
我不知道其他库的情况——volshow看起来很好,但我还没有测试过——但你可以在vtk中完成这个操作。
我一直在努力将其应用到mayavi中(请参见如何在mayavi中直接设置RGB / RGBA颜色),但对于某些图像源,mayavi以一种未设计处理此类问题的方式构建了vtk管道。尝试使用mlab.imshow将2D vtk.ImageData转换为真正的颜色时,每一步都遇到了阻力,但我最终还是做到了。
首先,以下是我如何在mayavi中使用mlab完成此操作。即使对于我的标准来说,这也太过hacky和“魔法”依赖:
from mayavi import mlab
import numpy as np
from tvtk.api import tvtk

k=mlab.imshow(np.random.random((10,10)),colormap='bone')

colors=tvtk.UnsignedCharArray()
colors.from_array(np.random.randint(256,size=(100,3)))
k.mlab_source.dataset.point_data.scalars=colors
k.actor.input.point_data.scalars=colors
#the latter set of scalars is what is actually used in the VTK pipeline in this 
#case, but if they don't play nice with the mayavi source then tvtk will
#complain because we are circumventing the structure it expects
k.actor.input.scalar_type='unsigned_char'
k.actor.input.number_of_scalar_components=3
k.image_map_to_color.lookup_table=None

k.actor.input.modified()
mlab.draw()
#this draw fails.  As it fails, there is an interaction here, somewhere deep in 
#tvtk, causing the ImageData to partially reset.. I have not been able to track 
#it down yet.  ignore the error output

k.actor.input.scalar_type='unsigned_char'
k.actor.input.number_of_scalar_components=3
#now after we reset these back to what they should be, it works

mlab.draw()
mlab.show()

但在纯tvtk中,情况并不那么糟糕:
import numpy as np
from tvtk.api import tvtk

colors=np.random.randint(256,size=(100,3))

an_image=tvtk.ImageData()
an_image.number_of_scalar_components=3
an_image.scalar_type='unsigned_char'
an_image.point_data.scalars=tvtk.UnsignedCharArray()
an_image.point_data.scalars.from_array(colors)
an_image.dimensions=np.array((10,10,1))

an_actor=tvtk.ImageActor()
an_actor.input=an_image
an_actor.interpolate=False

ren=tvtk.Renderer()
renWin=tvtk.RenderWindow()
renWin.add_renderer(ren)
ren.add_actor2d(an_actor)
iren=tvtk.RenderWindowInteractor()
iren.render_window=renWin
iren.interactor_style=tvtk.InteractorStyleTrackballCamera()
renWin.render()
iren.start()

当然,在vtk中完成这项工作需要更多的工作。你甚至可以将其包装得漂亮一些,使其变得合理。

我希望修复mayavi以正确处理这个问题,但正如你从我的片段中所看到的那样,这并不是一件简单的事情,可能需要一段时间。


非常感谢提供的信息。最终我选择了@cgohlke的建议,发现它更简单易行,但直接使用Mayavi/VTK设置RGBA颜色的方法非常有用 - 在未来处理更复杂的工作时可能会用到。 - ali_m

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接