要使用 Matplotlib 顯示圖片非常簡單,只要指定像素資料就可以了,例如:
import matplotlib.pyplot as plt
plt.title('face')
plt.imshow([
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [255, 0, 0], [255, 255, 255], [255, 0, 0], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]],
[[255, 255, 255], [255, 0, 0], [255, 0, 0], [255, 0, 0], [255, 255, 255]],
[[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255]]
])
plt.show()
其中每個 [255, 255, 255]
結構的資料,代表一個像素的 [R, G, B]
值,這會顯示以下的結果:
如果只想顯示灰階圖,每個像素指定一個數字代表階數,呼叫 imshow
時指定 cmap
(Color Map)為 cm.gray
:
import matplotlib.pyplot as plt
from matplotlib import cm
plt.title('face')
plt.imshow([
[255, 255, 255, 255, 255],
[255, 0, 255, 0, 255],
[255, 255, 255, 255, 255],
[255, 0, 0, 0, 255],
[255, 255, 255, 255, 255]
], cmap = cm.gray)
plt.show()
這會顯示以下的結果:
如果想讀入圖片顯示呢?Python 內建的 PIL
模組提供了圖片載入功能,透過 Image
讀入圖片後的資料,是以 [R, G, B]
形式代表每個像素,直接餵給 imshow
就可以了,例如讀入我的吉祥物圖片並顯示:
from PIL import Image
import matplotlib.pyplot as plt
img = Image.open('caterpillar.jpg')
img.load()
plt.title('caterpillar')
plt.imshow(img)
plt.show()
這會顯示以下的結果:
單純只是介紹圖片的讀入與顯示沒什麼太大意義,不如來看個二維 Perlin 雜訊的應用吧!基本原理與〈NumPy 與 Perlin 雜訊〉中談到相同,只不過二維 Perlin 雜訊在每個座標點會生成一個梯度,四個座標點的梯度,決定了四個座標點間的雜訊變化。
就結論而言,以下的程式將雜訊作為灰階度,並交給 imshow
顯示:
from math import floor
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
# Perlin noise 實作
def blending(t):
return 6 * (t ** 5) - 15 * (t ** 4) + 10 * (t ** 3)
def lerp(g1, g2, t):
return g1 + t * (g2 - g1)
def grad2(hashvalue, dx, dy):
return [dy, dx + dy, dx, dx - dy, -dy, -dx - dy, -dx, -dx + dy][hashvalue % 8];
rand_table = np.random.randint(255, size = 256).tolist()
def _perlin2(x, y):
xi = floor(x)
yi = floor(y)
aa = rand_table[
(rand_table[xi % 256] + yi) % 256
]
ba = rand_table[
(rand_table[(xi + 1) % 256] + yi) % 256
]
ab = rand_table[
(rand_table[xi % 256] + yi + 1) % 256
]
bb = rand_table[
(rand_table[(xi + 1) % 256] + yi + 1) % 256
]
dx = x - xi
dy = y - yi
u = blending(dx)
v = blending(dy)
g1 = lerp(grad2(aa, dx, dy), grad2(ba, dx - 1, dy), u)
g2 = lerp(grad2(ab, dx, dy - 1), grad2(bb, dx - 1, dy - 1), u)
return lerp(g1, g2, v)
_perlin2 = np.frompyfunc(_perlin2, 2, 1)
def perlin2(x, y):
cx, cy = np.meshgrid(x, y)
return _perlin2(cx, cy).astype(np.float)
# 將雜訊當成灰階度顯示為圖片
width = 500
x = np.arange(width) / 100
y = np.arange(width) / 100
noise = (perlin2(x, y) + 1) * 125
plt.title('Perlin noise')
plt.imshow(noise, cmap = cm.gray)
plt.show()
就 NumPy 的認識本身來說,這邊的範例需要知道的有兩個部份,一是 np.meshgrid
的使用,因為範例中的 perlin2
接受兩個 NumPy 陣列,這兩個陣列要用來計算出每個像素座標,由於 _perlin2
被向量化了,你必須提供兩個匹配的陣列,讓 _perlin2
每次抓對來進行運算,抓對的結果要代表像素座標,透過 np.meshgrid
,就可以將兩個軸的陣列,轉換為抓對用的陣列。
另一個要知道的部份是,rand_table
是 np.random.randint(255, size = 256).tolist()
,也就是它是個 list
,為什麼不使用 NumPy 陣列呢?因為 _perlin2
每次會對 rand_table
指定單一索引,對於這種情況,使用 NumPy 陣列反而緩慢(NumPy 陣列可以快速,是每次呼叫時,能處理一組連續資料的場合),透過 list
會是比較好的選擇。
以上範例的執行結果會是:
若你是第一次看到二維的 Perlin 雜訊以灰階圖表示,可能會想這是什麼神秘圖案?若將灰階數 0 作為低點,灰階數 255 作為高點,這就相當於起伏的地形。
談到地形,就會想到等高線,可以將等值的點連接起來,這可以透過 Matplotlib 的 contour
函式來繪製,將以上範例的 plt.imshow(noise, cmap = cm.gray)
改為:
plt.contour(noise)
就可以顯示以下的圖案:
等值線的層次,可以用 levels
來控制,可以指定數字決定層次數,或者指定類陣列(array-like)物件,決定哪些等值下要繪製輪廓線,例如設個 25 階:
plt.contour(noise, levels = 25)
就會看到更密集的等高線了: