python中噪音是什么意思_Perlin噪声和Python的ctypes
最近在知乎上看了一篇關(guān)于用C++加速Python的短文,受益匪淺。同時(shí)也受到啟發(fā),撰寫此文作為以后的參考。
作為Python的用戶經(jīng)常碰到的一個(gè)問題就是速度太慢,一般來說速度下降的一個(gè)主要原因是來自多重的for循環(huán)。如何給現(xiàn)有的Python代碼加速其實(shí)是Python用戶的一門必修課。比較熟悉numpy的用戶,可以熟練地寫成矩陣化操作,這樣可以大大加速運(yùn)行的效率。這和MATLAB里多用矩陣操作而少用for循環(huán)是一個(gè)道理。但往往我們中的大多數(shù)并沒有高超的numpy技巧。即便是有,Python代碼的閱讀性可能反而會(huì)下降。就算退一步講,寫成了比較好的numpy的代碼也未必會(huì)比C/C++的代碼快。在不放棄Python語(yǔ)言的前提下,怎么用C語(yǔ)言來提速呢?方法有很多,前面的所指的短文已經(jīng)給出了一個(gè)答案。這里我再用一個(gè)例子做一個(gè)簡(jiǎn)單的說明,希望對(duì)大家有所幫助。首先代碼都可以取到,https://github.com/yanfeit/PerlinNoise?github.com
關(guān)于Perlin噪聲,我就不詳細(xì)介紹。簡(jiǎn)單地說來,Perlin噪聲具有光滑性,自然性和隨機(jī)性的特點(diǎn)。感興趣的讀者可以找到很多相關(guān)資料,在這里我推薦兩個(gè),pvigier的GitHub site和Adrian's 的博客。Pvigier的Perlin噪聲是用numpy來實(shí)現(xiàn)的,讀者如果對(duì)自己numpy的技巧深感自信,可以去閱讀一下他寫的代碼。閱讀他的代碼之后,我們可以給自己兩個(gè)問題:我是否可以寫出這樣高度矩陣化操作的numpy代碼?
是否我們遇到的所用問題都可以用numpy矩陣化的操作來解決?
我想讀者心中可能也會(huì)犯嘀咕,確實(shí),高度矩陣化的操作需要程序員有高超的numpy技巧。反正我自愧不如,認(rèn)為我很難寫出那樣漂亮的代碼。我們?cè)賮砜纯碅drian的博客,這是篇博客文中的上乘之作, 是關(guān)于Perlin噪聲的一個(gè)詳細(xì)介紹,配合C#來實(shí)現(xiàn)。我想大多數(shù)讀者可能和我一樣,對(duì)寫成for循環(huán)的形式感到極度舒適。而閱讀這樣的代碼我想讀者們也是駕輕就熟吧。所以我首先制作了一個(gè)Perlin噪聲的C++代碼(其實(shí)只用到了C的成分),之后我們會(huì)使用ctypes來調(diào)用動(dòng)態(tài)鏈接庫(kù)的代碼。
創(chuàng)建動(dòng)態(tài)鏈接庫(kù)
$ mkdir build
$ cd build
$ cmake ..
$ make
$ mv ./lib/libperlinNoise.dylib ../python # Move library to the python folder.
$ cd ../python
$ python caltime.py # compare the time usage of numpy and C code.
為了快速測(cè)試一下效果,讀者可以嘗試執(zhí)行以上的代碼。
用了make之后我們會(huì)在/build/lib目錄下得到一個(gè)libperlinNoise.dylib的動(dòng)態(tài)鏈接庫(kù)文件,在這個(gè)庫(kù)里面我們可以調(diào)用兩個(gè)函數(shù)。它們的接口如下所示,
// 你可以在./lib/PerlinNoise.h的文件中找到相應(yīng)代碼。// 純粹為了解釋一下Float#ifndef PRECISION#define PRECISION 1#endif#if PRECISION==1typedef float Float;
#elsetypedef double Float;
#endif// .....省略很多代碼......// lattice 是指生成梯度所在的格子的大小,x,y,z當(dāng)然指的是方向// res 是指resolution,解析度(像素)。這里我與Pvigier的定義是不同的。Float* perlinNoise3D(int lattice_x, int lattice_y, int lattice_z,
int res_x, int res_y, int res_z);
Float* perlinNoise2D(int lattice_x, int lattice_y, int res_x, int res_y);
兩個(gè)函數(shù)返回的是指向Float的指針,我選用了單精度的浮點(diǎn)數(shù)也就是float。這里面有個(gè)需要注意的地方,函數(shù)切記不要返回指向一個(gè)超過二維數(shù)組的指針,其實(shí)根本就沒有這樣的定義,具體請(qǐng)看這個(gè)帖子。有了libperlinNoise.dylib這個(gè)動(dòng)態(tài)鏈接庫(kù)之后,剩下的任務(wù)就交給Python了。以下是我的代碼的一部分(借鑒了Pvigier的代碼,在./python/cppnoise.py中可以找到相應(yīng)的代碼),
# 我們所采用加速的方法,ctypes是build-in package
import ctypes
# 因?yàn)槲覀円祷刂羔?#xff0c;我覺得調(diào)用numpy下的這個(gè)包是我找到的比較簡(jiǎn)單可行的方法
from numpy.ctypeslib import ndpointer
# ...省略...其余的包的調(diào)用....
def octavePerlin2d(lattice, res, octaves = 1, persistence=0.5):
# pass the dynamic library
lib = ctypes.CDLL('./libperlinNoise.dylib')
# get the 2d Perlin noise function
perlinNoise2D = lib.perlinNoise2D
# Need specify the types of the argument for function perlinNoise2D
perlinNoise2D.argtypes = (ctypes.c_int, ctypes.c_int,
ctypes.c_int, ctypes.c_int)
# This note is extremely useful to understand how to return a 2d array!
# https://stackoverflow.com/questions/43013870/
# how-to-make-c-return-2d-array-to-python?noredirect=1&lq=1
# We can never pass a 2d array, therefore return 1d array in a C function
perlinNoise2D.restype = ndpointer(dtype=ctypes.c_float,
shape = (res[0], res[1]))
noise = np.zeros(res)
frequency = 1
amplitude = 1
for _ in range(octaves):
temp = perlinNoise2D(ctypes.c_int(frequency*lattice[1]),
ctypes.c_int(frequency*lattice[0]),
ctypes.c_int(res[1]),
ctypes.c_int(res[0]) )
noise += amplitude * temp
frequency *= 2
amplitude *= persistence
return noise
相比于木盞的函數(shù),我這里相對(duì)來說復(fù)雜一點(diǎn)點(diǎn)。我們需要注意的是,我們得告訴函數(shù)傳入的參數(shù)的類型和返回的類型和大小,這點(diǎn)至關(guān)重要。
# 讀取動(dòng)態(tài)鏈接庫(kù),
lib = ctypes.CDLL('./libperlinNoise.dylib')
# 從句柄中拿到PerlinNoise2D的函數(shù)
perlinNoise2D = lib.perlinNoise2D
# 告訴函數(shù)傳入?yún)?shù)的個(gè)數(shù)和類型
perlinNoise2D.argtypes = (ctypes.c_int, ctypes.c_int,
ctypes.c_int, ctypes.c_int)
# 告訴函數(shù)返回的類型,是c_float。shape是optinal的。
perlinNoise2D.restype = ndpointer(dtype=ctypes.c_float,
shape = (res[0], res[1]))
# 調(diào)用的方法非常簡(jiǎn)單。
temp = perlinNoise2D(ctypes.c_int(frequency*lattice[1]),
ctypes.c_int(frequency*lattice[0]),
ctypes.c_int(res[1]),
ctypes.c_int(res[0]) )
在Python中調(diào)用動(dòng)態(tài)鏈接庫(kù)后得到的加速效果(當(dāng)然我在C++用了單精度的float),讀者可以自行修改成雙精度去測(cè)試一下。
$ python caltime.py
2D noise, numpy time consuming: 0.08261830806732177
3D noise, numpy time consuming: 4.525643992424011
2D noise, cpp time consuming: 0.007184123992919922
3D noise, cpp time consuming: 0.1645211935043335
我們可以發(fā)現(xiàn)C語(yǔ)言的代碼可以說快了將近10倍以上。
最后上個(gè)圖,以饗讀者。
總結(jié)
以上是生活随笔為你收集整理的python中噪音是什么意思_Perlin噪声和Python的ctypes的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 9. 信息安全标准与法规
- 下一篇: 计算机组成原理-算术逻辑单元(ALU)