在MATLAB中可以通过使用 surf
函数绘制三维面,在 matplotlib
中则对应 plt.plot_surface
函数。
使用矩阵表示面
想象空间有中一个限大小的面,这个面上的每个点都有自己的三维空间坐标 $(x, y, z)$ 以及一个着色值 $c$。
那么如果需要绘制三维空间中的曲面,则描述这个曲面需要四个二维矩阵,分别表示面中每个点的三维空间坐标与着色值。这里的二维矩阵可以看做是在数组空间中的投影。
MATLAB 绘制面
在MATLAB中,可以通过 surf
函数绘制三维面,直接看例子:
x = 1:100;
x = sort(1./x);
x = ones(50,1) * x;
y = sin((1:100)/pi)/10;
y = ones(50,1) * y;
z = (1:50)' * ones(1,100);
color = (1:50)' * ones(1,100);
color(25:45, 75:95) = color(25:45, 75:95)';
surf(x, y ,z,color,'LineStyle', 'none', 'FaceColor', 'interp')
这里简单对$x,y,z$矩阵给定了值,并给定了变化的着色值$c$(color
),在MATLAB中可以比较便捷地在surf
函数中使用 'FaceColor', 'interp'
对面中的着色进行插值,以达到较为平滑的着色过渡效果。
Python 使用 plt.plot_surface 绘制面
在 Python 中绘制,首先需要引入 matplotlib
并声明一个三维 ax
:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors as colors
import matplotlib.cm as cmx
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
同样给定 $x, y, z$ 矩阵以及着色矩阵 $color$:
x = np.arange(100)+1
x = np.sort(1/x)
x = np.ones((50,100)) * x
y = np.sin(np.arange(100)+1)/10;
y = np.ones((50,100)) * y
z = [np.arange(50) for ii in range(100)]
z = np.array(z).T
color = z.copy()
color[25:45, 75:95] = color[25:45, 75:95].T
值得注意的是,这里需要将color
映射到RGBA色彩空间,参考色条映射方案。
colors_map = plt.get_cmap('jet_r')
cNorm = colors.Normalize(vmin=np.min(color), vmax=np.max(color))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=colors_map)
my_col = scalarMap.to_rgba(color)
之后对面进行绘制:
ax.plot_surface(x, y, z, facecolors=my_col, cmap=colors_map)
常用可选参数
antialiased
可以在绘图时添加 antialiased=True
对表面进行平滑。
ax.plot_surface(x, y, z, facecolors=my_col, cmap=colors_map, antialiased=True)
alpha
可以调整绘图面的透明度。
ax.plot_surface(x, y, z, facecolors=my_col, cmap=colors_map, alpha=False)
rstride
与 cstride
定义二维矩阵两个方向的采样间隔,plt.plot_surface
默认采样间隔为 10
,即如果给定 $1000\times1000$ 的数据矩阵,实际绘制仅为$100\times100$。因此如果需要高精度绘图,则需要指定rstride=1, cstride=1
。
ax.plot_surface(x, y, z, facecolors=my_col, cmap=colors_map, rstride=1, cstride=1)
插值方法
相比于MATLAB自带的插值功能,在分辨率较低的情况下,antialiased=True
往往并不能达到预期的效果。因此如果需要插值,应当在绘图前同时对四个矩阵进行插值,可以使用scipy.ndimage.interpolation.zoom 对数据进行插值:
from scipy.ndimage.interpolation import zoom
x = zoom(x,[10,4],order=5)
z = zoom(z,[10,4],order=5)
y = zoom(y,[10,4],order=5)
color = zoom(color,[10,4],order=1)
zoom 函数的第二个参数为插值放大的比例,以上代码两个方向分别插值放大了10倍与4倍。 同时可以通过order
参数对样条插值的阶数进行指定(0~5),默认为3。