3D曲面可视化
最近需要 visualize 一些三維的曲面數據,于是簡單調查了一下 Python 繪制三維曲面的一些常用的辦法,貼在這里以免自己將來再需要用到的時候又想不起來了。比如我們想要畫這個樣子的 3D 曲面圖。
方便起見,我們假造一個 user case,假設??和??是兩個 hyper parameter,現在你使用 grid search 的方式嘗試了每個參數組合下的模型效果。
import numpy as npdef eval_params(x, y): # ...# Make data. X = np.arange(-5, 5, 0.25) Y = np.arange(-5, 5, 0.25)Z = np.zeros((len(X), len(Y))) for i in range(len(X)): for j in range(len(Y)): Z[i, j] = eval_params(X[i], Y[j])現在我們希望把這個參數搜索的曲面畫出來,看看最大值最小值、曲面的連續性之類的性質,所以我們需要畫一個 3D surface 圖。比較新版本的 Matplotlib 其實已經有挺好的 3D 繪圖支持。上面的示例圖其實就是在 Matplotlib 里畫的。
import matplotlib from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from matplotlib import cmfig = plt.figure() ax = fig.gca(projection='3d')XX, YY = np.meshgrid(X, Y)# Plot the surface. surf = ax.plot_surface(XX, YY, Z, cmap=cm.coolwarm, linewidth=0, antialiased=True)# Add a color bar which maps values to colors. fig.colorbar(surf, shrink=0.5, aspect=5)plt.savefig('surface.pdf')其中?import Axes3D?那句雖然沒有直接用到,但是如果刪掉的話在創建 3D projection 的 axes 的時候就會出錯。plot_surface?這個函數負責繪制 3D surface,參數相當 straightforward。因為?Z?作為我們 evaluate 參數的結果是一個二維的矩陣,所以?plot_surface?需要的前兩個參數也是二維矩陣,并且需要是和?Z?大小相同。
簡單來說,對于一組 index?(i, j),XX[i, j],?YY[i, j]?和?Z[i, j]?一起給出了一個位于曲面上的點的三維坐標。而?meshgrid?這個函數就是用于方便地根據原來的?X?和?Y?來創建這樣的二維矩陣的。
到這里我們的目標其實已經達到了。如果把上面的?savefig?換成?plt.show()的話,會彈出一個對話框來顯示 3D 曲面圖,好處在于可以對曲面進行旋轉、縮放等交互操作,有時候對于看到曲面被擋住的部分來說非常有用。不過 Matplotlib 的 3D 操作交互反應速度相當緩慢,可能是沒有用任何硬件加速之類的簡單實現吧。如果你不能忍受那個緩慢的交互速度,或者需要將生成出來的圖片發給別人看,而對方又不一定是能運行 Python 代碼的,那還有一個選擇就是生成內嵌 3D 對象的 PDF 文件。PDF 文件可以通過?PRC?格式內嵌 3D 圖像模型,不過目前只有 Adobe 家的閱讀器(比如 Adobe Reader 或者 Adobe Acrobat)可以查看這種格式的對象。在 Acrobat 里打開大概長下面這個樣子,可以對 3D 曲面進行各種操作和檢查。
不過 Matplotlib 本身似乎并不支持直接輸出這種內嵌 3D 對象的 PDF 文件。這里我們可以借助另外一個叫做?Asymptote?的繪圖工具。它其實是一種繪圖語言,和 LaTeX 結合得比較緊密,但是和?PGF/TikZ?或者是?PSTricks?不一樣的是,它并不是實現為 TeX 的宏包,所以它的語法并不是一些看起來像英語一樣的很飄逸但是一不小心就會語法錯誤的?DSL,而是有點類似于 C 語言語法。很多年前用它畫過一些 3D 的 vector field 之類的圖,感覺比較適合畫一些復雜的 scientific 的 illustration,可以看一下它的示例。
當然我一般不會用它來 plot 數據圖,不過這里可以借用一下的是它能輸出內嵌 3D 對象的 PDF 文件的功能。Asymptote 畫圖的簡單流程是創建一個?foo.asy?文件,把命令寫在里面,然后執行
asy -f pdf -o foo.pdf foo.asy就可以了。Asymptote 里構建一個 3D surface 可以通過一個叫做?surface?的命令來實現,它的第一個參數是一個描述 surface 的函數:輸入是??兩個 index,輸出是一個三維坐標?。這個比較適合畫一些 parametric function,不過這里我們是直接 plot 數據,其實只要做一個簡單的矩陣查表函數就可以了。Asymptote 里最簡單的讀取數據的方法是讀入空白分隔的文本文件,打開文件之后只要對一個數組或者矩陣進行賦值就可以讀入一行或者一塊數據:
import graph3; import palette;size3(150, IgnoreAspect);// Open the data file file in=input("data.txt").line(); // read file by assignment // this read a line of numbers as an array real[] x=in; // first line real[] y=in; // second line// read the rest of the file as a matrix // 0, 0 means do not restrict the number of // elements to read in either dimension real[][] z=in.dimension(0,0);triple f(pair t) { int i = round(t.x); int j = round(t.y); return (x[i], y[j], z[i][j]); }數據文件格式非常簡單,頭兩行分別是兩個參數??和??的值列表,然后剩下的行是整個數據矩陣??的一個表格。通過 Numpy 的?savetxt?函數很容易導出。在 Asymptote 里讀入數據之后我們就可以構造查表函數??了,從上面可以看到??只是直接通過下標取得相應的坐標而已。接下來就可以直接構造 surface 并進行繪制了:
surface s = surface(f, (0,0), (x.length-1, y.length-1), x.length-1, y.length-1); s.colors(palette(s.map(zpart), Rainbow()));draw(s, meshpen=blue);triple m=currentpicture.userMin(); triple M=currentpicture.userMax(); triple target=0.5*(m+M); currentprojection=perspective(camera=target+realmult(dir(60,210),M-m), target=target);xaxis3(Label("$\lambda_1$",position=MidPoint,align=-Y-Z), Bounds(),blue,OutTicks(Step=0.25,step=0.05)); //Bounds(),blue,OutTicks(Label(align=-Y-X))); yaxis3(Label("$\lambda_2$",position=MidPoint,align=-5X), Bounds(),red,OutTicks(Step=0.25,step=0.05)); zaxis3("Error",Bounds(),black,OutTicks(Step=0.25,step=0.05));代碼非常直接,唯一需要簡單注意一下的是我們在調用?surface?函數的時候,傳遞了參數?(0,0)?和?(x.length-1, y.length-1),這里對應??在查表時的輸入序列。
這樣就大功告成了,當然缺點就是 Adobe 家族以外的 PDF 閱讀器都看不了。第三個對于 viewer 來說比較 user friendly 的選項就是通過 web 頁面。現在在 webGL 之類的技術支持下,其實 web 頁面里渲染 3D 對象的效果其實已經非常好了。
比如之前在網上看到這個 3D 的卡通畫,簡直太驚艷了,我覺得現在的各種 3D 游戲之類的都處于嚴重的?Uncanny Valley?之中,讓我非常難以接受,相反若是做成這種風格,應該會(讓我覺得)賞心悅目很多。然而雖然看起來只是簡單的 line drawing 的結果,但是實際的制作過程還是要進行正兒八經的三維建模、渲染之類的,作者在這里給了制作過程介紹,看起來似乎還是相當費力的。另外還有這里也有一些非常漂亮的 3D 線條漫畫,看起來都非常酷,而且都是直接在瀏覽器了渲染出來的,不需要額外安裝什么很復雜的 viewer 。
就我所知范圍,目前的繪圖工具中似乎?plotly?算是對 webgl 支持比較好并且也簡單易用的。它們的 3D 圖大概長下面這個樣子,這里有一個在線的例子可以直接進行交互,渲染和交互效果都挺好的。
例如直接用我們剛才的 Python 里的數據的話,用下面的代碼可以直接生成 plotly 的圖,結果保持成一個 HTML 文件。文件里內嵌的 viewer,應該用任意一個現代一點的瀏覽器都能打開預覽和交互。
import plotly import plotly.graph_objs as godata = [go.Surface(z=Z, x=XX, y=YY)] layout = go.Layout(title='Test Plot', autosize=True) fig = go.Figure(data=data, layout=layout)# save offline standalone HTML files plotly.offline.plot(fig, filename='test-plot')唯一的缺點可能就是由于內嵌的 viewer,所以 HTML 文件還是比較大的,一個簡單的 surface plot 大概也需要 2M 左右。另外 plotly 還能內嵌在 Jupyter Notebook 里預覽交互圖,它還提供收費的云服務可以在服務器上 host 你的 figure。
感覺以后的科技論文格式應該允許更多的交互內容,嵌入視頻、音頻、交互圖表之類的,還有更全的 meta data,圖表、公式、引用的預覽、跳轉等等。然而現實是目前很多地方還會要求 plot 必須是在黑白打印的情況下也能分辨的。
總結
- 上一篇: Hough 圆变换----Matlab实
- 下一篇: Windows 7防火墙设置详解(三)