HOWTO · Matplotlib
Python 中將 NumPy 陣列轉換為 PIL 影象
本教程解釋了我們如何使用 PIL 包中的 Image.fromarray()將 NumPy 陣列轉換為 PIL 影象。
本頁內容
使用 Pillow 的 Image.fromarray(),即可從 NumPy 陣列建立 PIL 影像。二維陣列代表灰階像素;形狀為 (height, width, 3) 或 (height, width, 4) 的陣列則代表 RGB 或 RGBA 像素。轉換前也要確認陣列的 dtype 和數值。PIL 是常見的匯入名稱,而 Pillow 是目前持續維護的函式庫。
若要建立 RGB 影像,請建立三通道的 uint8 陣列,再傳給 Image.fromarray()。輸出會顯示影像模式、(width, height) 尺寸,以及左上角的像素值。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import numpy as np
from PIL import Image
pixels = np.array(
[[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 255]]],
dtype=np.uint8,
)
image = Image.fromarray(pixels)
print(image.mode, image.size, image.getpixel((0, 0)))
RGB (2, 2) (255, 0, 0)
建立的影像是儲存在記憶體中的 Pillow 物件。你可以檢查它的模式和尺寸,再於支援影像預覽的環境中顯示,或將它儲存為檔案。
使用 Image.fromarray() 將 NumPy 陣列轉換為 PIL 影像
Image.fromarray(array) 會根據陣列的形狀和 dtype 推斷影像模式。灰階資料使用形狀為 (height, width) 的二維陣列;RGB 資料使用 (height, width, 3),若再加上第四個通道則為 RGBA。前兩個維度分別代表列和欄,因此 40 列、60 欄的陣列會建立寬 60 像素、高 40 像素的影像。
如果數值已是 0 到 255 的整數,且代表一般 8 位元影像樣本,請使用 np.uint8。不要不加判斷就轉型:浮點數轉型會截去小數部分,超出可表示範圍的值則可能溢位回繞。若資料使用其他刻度,請先決定轉換前要如何映射到顯示亮度。例如,介於 0–1 的資料可以乘以 255 並四捨五入;其他範圍則需依資料意義明確正規化。
準備陣列的形狀、dtype 與數值範圍
形狀為 (height, width) 的陣列代表灰階影像。RGB 和 RGBA 陣列會多出最後一個通道維度,每個像素分別有三個或四個數值。像 (height, width, 1) 這樣的形狀並非一般的二維灰階格式;如果最後那個單一維度代表灰階通道,可用 array[..., 0] 移除。轉換前請檢查 array.shape、array.dtype,以及最小值和最大值。陣列必須是規則的矩形格狀資料;每列長度不一的巢狀清單並不是影像資料。
以下是完整的灰階轉換範例。範例值直接採用 8 位元範圍,因此不需要縮放。產生的影像使用灰階模式 L,尺寸為 (3, 2)。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import numpy as np
from PIL import Image
gray = np.array([[0, 64, 128], [32, 160, 255]], dtype=np.uint8)
image = Image.fromarray(gray)
print(image.mode, image.size)
L (3, 2)
如果來源是介於 0 到 1 的浮點數陣列,可以使用 np.round(array * 255).astype(np.uint8),但請先確認轉換方式符合資料意義。只有當這些值是正規化後的強度值時,這種轉換才合適。若資料代表溫度或任意分數等測量值,請選擇有意義的上下界,將資料截限或正規化到該範圍,並記錄映射方式;否則對比和像素值可能會造成誤解。
建立 PIL 影像前先套用 Matplotlib 色彩映射
如果來源是二維純量場,而你想用顏色表示數值,可以套用 Matplotlib 色彩映射。先將資料正規化至所選色彩映射預期的區間,再把映射結果轉為 8 位元 RGB 或 RGBA 陣列。plasma 色彩映射會回傳正規化後的紅、綠、藍和 alpha 通道;使用 [..., :3] 去除 alpha 通道後,即可得到 RGB 陣列。這是 Image.fromarray() 之外的獨立步驟:色彩映射負責產生顏色,Pillow 則將這些像素包裝成影像。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import numpy as np
import matplotlib
from PIL import Image
values = np.array([[0.0, 0.25], [0.75, 1.0]])
colors = matplotlib.colormaps["plasma"](values)[..., :3]
rgb = np.round(colors * 255).astype(np.uint8)
image = Image.fromarray(rgb)
print(image.mode, image.size)
RGB (2, 2)
此範例假設 values 已正規化至 0 到 1。如果實際純量範圍不同,請先根據適當的資料界限進行正規化,再套用色彩映射。色彩映射適合用於視覺化,但它會將純量值轉成顯示用顏色;如果分析時仍需要原始測量值,請保留原始資料。產生的影像為 RGB,尺寸是 (2, 2)。
調整 PIL 影像尺寸並儲存
轉換後可使用 image.resize((width, height)) 調整尺寸;Pillow 接受的尺寸順序是先寬、後高。儲存和顯示是不同操作:show() 會使用本機環境開啟預覽,而 save() 會寫入影像檔案。請選擇符合所需格式的副檔名,例如無損輸出的 .png 或 JPEG 的 .jpg。
本節中的程式碼以及顯示的標籤或輸出仍為英文。
import numpy as np
from PIL import Image
pixels = np.zeros((20, 30, 3), dtype=np.uint8)
pixels[:, :15] = [255, 80, 0]
pixels[:, 15:] = [20, 90, 220]
image = Image.fromarray(pixels)
resized = image.resize((60, 40))
resized.save("resized-image.png")
print(resized.size)
範例會在記憶體中建立輸入影像,並將 resized-image.png 寫入目前工作目錄。實際應用程式中,請使用符合輸出位置的路徑。儲存不會修改 NumPy 陣列,而調整尺寸會改變 Pillow 影像的像素尺寸。
常見的轉換問題
- **影像模式不如預期或形狀遭拒:**檢查
array.shape。灰階使用(height, width);RGB 或 RGBA 則在最後一個維度分別使用三個或四個通道。若有多餘的單一通道,請將它移除;若來源採用不同的通道慣例,則需重新排列通道。 - **亮度或顏色不對:**轉換前檢查數值範圍。若浮點數值介於 0–1,轉成 8 位元影像時需要適當縮放;若數值已介於 0–255,就不要再次乘以 255。其他範圍則應正規化至有記錄的界限。
- **dtype 不符合預期:**檢查
array.dtype,再決定如何轉換。單純轉型不會重新縮放資料;如果原始精度很重要,請保留來源副本。 - **色彩映射結果不對:**確認純量輸入已依預期正規化,並檢查結果是三個 RGB 通道還是四個 RGBA 通道。建立影像前,請將正規化後的色彩通道轉成
uint8。 - **沒有建立影像檔案:**使用可寫入的路徑和支援的副檔名呼叫
save()。只有需要預覽時才使用show();它不會儲存影像。