python绘制三维图像球_将图像映射到球体上并绘制三维轨迹

一旦你有了你的行星,使用^{}绘制轨迹就很容易了,所以我将集中精力用mayavi纹理映射一个行星到一个球体。(原则上,我们可以使用matplotlib来执行任务,但是性能和质量比mayavi差得多,请参见本文的结尾部分。)

好的场景:球体上的球体

事实证明,如果你想把一个球体参数化的图像映射到一个球体上,你必须把你的手弄脏一点,并使用一些裸vtk,但实际上有很少的工作要做,结果看起来很好。我要用Blue Marble image from NASA来演示。他们的自述说这些图片a geographic (Plate Carrée) projection, which is based on an equal latitude-

longitude grid spacing (not an equal area projection!)

在wikipedia中查找它,结果发现它也被称为equirectangular projection。换句话说,沿着x的像素直接对应于经度,y的像素直接对应于纬度。这就是我所说的“球参数化”。在

所以在这个例子中,我们可以使用一个低级的^{}来生成一个可以映射纹理的球体。我们自己构建球体网格可能会导致贴图中出现瑕疵(稍后将详细介绍)。在from mayavi import mlab

from tvtk.api import tvtk # python wrappers for the C++ vtk ecosystem

def auto_sphere(image_file):

# create a figure window (and scene)

fig = mlab.figure(size=(600, 600))

# load and map the texture

img = tvtk.JPEGReader()

img.file_name = image_file

texture = tvtk.Texture(input_connection=img.output_port, interpolate=1)

# (interpolate for a less raster appearance when zoomed in)

# use a TexturedSphereSource, a.k.a. getting our hands dirty

R = 1

Nrad = 180

# create the sphere source with a given radius and angular resolution

sphere = tvtk.TexturedSphereSource(radius=R, theta_resolution=Nrad,

phi_resolution=Nrad)

# assemble rest of the pipeline, assign texture

sphere_mapper = tvtk.PolyDataMapper(input_connection=sphere.output_port)

sphere_actor = tvtk.Actor(mapper=sphere_mapper, texture=texture)

fig.scene.add_actor(sphere_actor)

if __name__ == "__main__":

image_file = 'blue_marble_spherical.jpg'

auto_sphere(image_file)

mlab.show()

结果正是我们所期望的:

xa481.png

不太好的场景:不是球体

不幸的是,我无法想出如何使用上述方法的非球面映射。此外,我们可能不想在完美球体上映射,而是在椭球体或类似的圆形对象上映射。对于这种情况,我们可能需要自己构建曲面,并尝试在其上进行纹理映射。剧透警报:不会那么漂亮。在

从手动生成的球体开始,我们可以像以前一样加载纹理,并处理由mlab.mesh构造的高级对象:

^{pr2}$

正如您在代码中看到的注释一样,这里有一些注意事项。第一种是球面映射模式出于某种原因翻转输入图像(这会导致地球反射)。因此,使用这种方法,我们首先要创建输入图像的翻转版本。每个图像只需执行一次,但我将相应的代码块放在上面函数的顶部。在

第二个警告是,如果纹理映射器的prevent_seam属性保留为默认值1,则映射从0到180方位角发生,球体的另一半将获得反射贴图。我们显然不想这样:我们想从0到360方位映射整个球体。碰巧,这个映射可能意味着我们在phi=0的映射中看到了一个接缝(不连续),即在映射的边缘。这是在可能的情况下使用第一种方法的另一个原因。总之,下面是结果,包含phi=0点(证明没有接缝):

6zgKX.png

柱面映射

上述球面映射的工作方式是,曲面上的每个点都通过空间中的一个给定点投影到一个球体上。对于第一个例子,这个点是原点,对于第二个例子,我们可以设置一个3长度的数组作为cylinder_mapper.center的值,以便映射到非原点中心的球体上。在

现在,你的问题提到了一个圆柱形映射。原则上,我们可以使用第二种方法:mesh.actor.tcoord_generator_mode = 'cylinder'

cylinder_mapper = mesh.actor.tcoord_generator

cylinder_mapper.automatic_cylinder_generation = 0 # use manual cylinder from points

cylinder_mapper.point1 = np.array([0,0,-R])

cylinder_mapper.point2 = np.array([0,0,R])

cylinder_mapper.prevent_seam = 0 # use 360 degrees, causes seam but no fake data

这会将球形贴图更改为圆柱形。它根据两个点([0,0,-R]和[0,0,R])定义投影,这两个点设置圆柱体的轴和范围。每个点都是根据它的圆柱坐标(phi,z):从0到360度的方位角和坐标的垂直投影。先前关于接缝的评论仍然适用。在

但是,如果我必须做这样的柱面映射,我肯定会尝试使用第一种方法。在最坏的情况下,这意味着我们必须将圆柱参数化的映射转换为球面参数化的映射埃德一号。同样,每个地图只需完成一次,并且可以使用2d插值轻松完成,例如使用^{}。对于特定的变换,您必须知道非球面投影的细节,但将其转换为球面投影应该不太困难,然后可以根据TexturedSphereSource的第一种情况使用它。在

附录:matplotlib

出于完整性的考虑,您可以使用matplotlib做任何您想做的事情,但这将需要更多的内存和CPU(请注意,您必须使用mayavi或matplotlib,但不能在一个图形中混合使用这两者)。其思想是定义一个与输入映射的像素相对应的网格,并将图像作为Axes3D.plot_surface的facecolors关键字参数传递。这种结构使得球体的分辨率与映射的分辨率直接耦合。我们只能使用少量的点来保持内存需求的可处理性,但是这样的结果看起来会很糟糕。不管怎样:import numpy as np

import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D

def mpl_sphere(image_file):

img = plt.imread(image_file)

# define a grid matching the map size, subsample along with pixels

theta = np.linspace(0, np.pi, img.shape[0])

phi = np.linspace(0, 2*np.pi, img.shape[1])

count = 180 # keep 180 points along theta and phi

theta_inds = np.linspace(0, img.shape[0] - 1, count).round().astype(int)

phi_inds = np.linspace(0, img.shape[1] - 1, count).round().astype(int)

theta = theta[theta_inds]

phi = phi[phi_inds]

img = img[np.ix_(theta_inds, phi_inds)]

theta,phi = np.meshgrid(theta, phi)

R = 1

# sphere

x = R * np.sin(theta) * np.cos(phi)

y = R * np.sin(theta) * np.sin(phi)

z = R * np.cos(theta)

# create 3d Axes

fig = plt.figure()

ax = fig.add_subplot(111, projection='3d')

ax.plot_surface(x.T, y.T, z.T, facecolors=img/255, cstride=1, rstride=1) # we've already pruned ourselves

# make the plot more spherical

ax.axis('scaled')

if __name__ == "__main__":

image_file = 'blue_marble.jpg'

mpl_sphere(image_file)

plt.show()

上面的count参数定义了贴图的下采样和渲染球体的相应大小。在上述180设置下,我们得到下图:

esMRk.png

此外,matplotlib使用2d渲染器,这意味着对于复杂的3d对象,渲染通常会产生奇怪的伪影(特别是,扩展对象可以完全在另一个对象的前面或后面,因此联锁的几何体通常看起来是不完整的)。考虑到这些,我肯定会使用mayavi绘制一个有纹理的球体。(尽管matplotlib中的映射在曲面上逐面工作,因此可以直接应用于任意曲面。)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值