Pillow 中的檔案處理¶
當開啟檔案作為影像時,Pillow 需要檔案名稱、os.PathLike
物件或類檔案物件。Pillow 使用檔案名稱或 Path
開啟檔案,因此本文的其餘部分都將它們視為類檔案物件。
以下都是等效的
from PIL import Image
import io
import pathlib
with Image.open("test.jpg") as im:
...
with Image.open(pathlib.Path("test.jpg")) as im2:
...
with open("test.jpg", "rb") as f:
im3 = Image.open(f)
...
with open("test.jpg", "rb") as f:
im4 = Image.open(io.BytesIO(f.read()))
...
如果將檔案名稱或類路徑物件傳遞給 Pillow,則在呼叫 Image.Image.load()
方法後,Pillow 開啟的結果檔案物件也可能由 Pillow 關閉,前提是相關的影像沒有多個影格。
Pillow 通常無法關閉並重新開啟檔案,因此任何對該檔案的存取都需要在關閉之前。
影像生命週期¶
Image.open()
檔案名稱和Path
物件會作為檔案開啟。中繼資料會從開啟的檔案讀取。該檔案會保持開啟以供進一步使用。Image.Image.load()
當需要影像的像素資料時,會呼叫load()
。目前的影格會讀入記憶體。現在可以使用影像,而無需依賴底層影像檔案。任何根據另一個影像建立新影像實例的 Pillow 方法都會在內部對原始影像呼叫
load()
,然後讀取資料。新的影像實例將不會與原始影像檔案相關聯。如果將檔案名稱或
Path
物件傳遞給Image.open()
,則該檔案物件是由 Pillow 開啟,並被視為由 Pillow 獨佔使用。因此,如果影像為單影格影像,則在此方法讀取影格後將關閉檔案。如果影像為多影格影像(例如,多頁 TIFF 和動畫 GIF),則影像檔案會保持開啟,以便Image.Image.seek()
可以載入適當的影格。Image.Image.close()
關閉檔案並銷毀核心影像物件。Pillow 上下文管理器也會關閉檔案,但不會銷毀核心影像物件。例如:
with Image.open("test.jpg") as img: img.load() assert img.fp is None img.save("test.png")
單影格影像的生命週期相對簡單。檔案必須保持開啟狀態,直到呼叫 load()
或 close()
函式或上下文管理器結束為止。
多影格影像更複雜。load()
方法不是終端方法,因此不應關閉底層檔案。一般來說,在呼叫者明確關閉影像之前,Pillow 不知道是否會有任何要求其他資料。
複雜情況¶
TiffImagePlugin
有一些程式碼可以將底層檔案描述子傳遞到 libtiff (如果在實際檔案上運作)。由於 libtiff 會在內部關閉檔案描述子,因此在將其傳遞到 libtiff 之前會複製該描述子。在檔案關閉後,需要檔案存取的操作將會失敗
with open("test.jpg", "rb") as f: im5 = Image.open(f) im5.load() # FAILS, closed file with Image.open("test.jpg") as im6: pass im6.load() # FAILS, closed file
建議的檔案處理¶
Image.Image.load()
應關閉影像檔案,除非有多個影格。Image.Image.seek()
永遠不應關閉影像檔案。程式庫的使用者應使用上下文管理器或對以檔案名稱或
Path
物件開啟的任何影像呼叫Image.Image.close()
,以確保底層檔案已關閉。