編寫您自己的影像外掛程式¶
Pillow 使用外掛程式模型,讓您可以在不變更程式庫本身的情況下,將自己的解碼器和編碼器新增至程式庫。這類外掛程式通常會有類似 XxxImagePlugin.py
的名稱,其中 Xxx
是唯一的格式名稱 (通常是縮寫)。
警告
Pillow >= 2.1.0 不再自動匯入 Python 路徑中任何名稱結尾為 ImagePlugin.py
的檔案。您需要手動匯入您的影像外掛程式。
Pillow 會分兩個階段解碼檔案
它會依載入順序迴圈處理可用的影像外掛程式,並使用檔案的前 16 個位元組呼叫外掛程式的
_accept
函式。如果_accept
函式傳回 true,則會呼叫外掛程式的_open
方法來設定影像中繼資料和影像圖塊。_open
方法並非用於解碼實際的影像資料。當要求影像資料時,會呼叫
ImageFile.load
方法,該方法會為每個圖塊設定解碼器並將資料饋送至其中。
影像外掛程式應包含衍生自 PIL.ImageFile.ImageFile
基底類別的格式處理常式。此類別應提供 _open
方法,該方法會讀取檔案標頭並至少設定內部 _size
和 _mode
屬性,以便填入 mode
和 size
。為了能夠載入檔案,此方法也必須建立 tile
描述項的清單,其中包含解碼器名稱、圖塊的範圍以及任何解碼器專屬的資料。必須透過呼叫 Image
模組,明確註冊格式處理常式類別。
注意
為了效能考量,_open
方法快速拒絕不包含適當內容的檔案非常重要。
範例¶
以下外掛程式支援簡單的格式,該格式具有 128 位元組的標頭,其中包含「SPAM」字詞,後接寬度、高度和像素大小 (以位元為單位)。標頭欄位以空格分隔。影像資料緊接在標頭之後,可以是雙層、灰階或 24 位元真彩色。
SpamImagePlugin.py:
from PIL import Image, ImageFile
def _accept(prefix: bytes) -> bool:
return prefix[:4] == b"SPAM"
class SpamImageFile(ImageFile.ImageFile):
format = "SPAM"
format_description = "Spam raster image"
def _open(self) -> None:
header = self.fp.read(128).split()
# size in pixels (width, height)
self._size = int(header[1]), int(header[2])
# mode setting
bits = int(header[3])
if bits == 1:
self._mode = "1"
elif bits == 8:
self._mode = "L"
elif bits == 24:
self._mode = "RGB"
else:
msg = "unknown number of bits"
raise SyntaxError(msg)
# data descriptor
self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 128, (self.mode, 0, 1))]
Image.register_open(SpamImageFile.format, SpamImageFile, _accept)
Image.register_extensions(
SpamImageFile.format,
[
".spam",
".spa", # DOS version
],
)
格式處理常式必須一律設定內部 _size
和 _mode
屬性,以便填入 size
和 mode
。如果未設定這些屬性,則無法開啟檔案。為了簡化外掛程式,呼叫程式碼會將 SyntaxError
、KeyError
、IndexError
、EOFError
和 struct.error
之類的例外視為無法識別檔案。
請注意,必須使用 PIL.Image.register_open()
明確註冊影像外掛程式。雖然非必要,但最好也註冊此格式使用的任何延伸檔名。
匯入外掛程式後,即可使用
from PIL import Image
import SpamImagePlugin
with Image.open("hopper.spam") as im:
pass
tile
屬性¶
為了能夠讀取檔案以及只是識別檔案,也必須設定 tile
屬性。此屬性包含圖塊描述項的清單,其中每個描述項都會指定應該如何將資料載入至影像中的指定區域。
在大多數情況下,只會使用單一描述項,涵蓋整個影像。PsdImagePlugin.PsdImageFile
會使用多個圖塊來合併單一層內的通道,因為通道會分別依序儲存。
圖塊描述項是具有以下內容的 4 元組
(decoder, region, offset, parameters)
欄位的使用方式如下
- 解碼器
指定要使用的解碼器。此處使用的
raw
解碼器支援各種像素格式的未壓縮資料。如需此解碼器的詳細資訊,請參閱下方的說明。可以在
_imaging.c
中函式陣列的編碼解碼器區段下查看 C 解碼器的清單。Python 解碼器會註冊在相關的外掛程式內。- 區域
指定要在影像中儲存資料的位置的 4 元組。
- 偏移
從檔案開頭到影像資料的位元組偏移。
- 參數
解碼器的參數。此欄位的內容取決於圖塊描述項元組中第一個欄位指定的解碼器。如果解碼器不需要任何參數,請對此欄位使用
None
。
請注意,tile
屬性包含圖塊描述項的清單,而不只是單一描述項。
解碼器¶
原始解碼器¶
raw
解碼器用於讀取影像檔案中的未壓縮資料。它可以用於大多數未壓縮檔案格式,例如 PPM、BMP、未壓縮 TIFF 和許多其他格式。若要搭配 PIL.Image.frombytes()
函式使用原始解碼器,請使用以下語法
image = Image.frombytes(
mode, size, data, "raw",
raw_mode, stride, orientation
)
在圖塊描述項中使用時,參數欄位應該看起來像這樣
(raw_mode, stride, orientation)
欄位的使用方式如下
- raw_mode
檔案中使用的像素配置,用於將資料正確轉換為 PIL 的內部配置。如需可用格式的摘要,請參閱下表。
- 跨距
影像中兩個連續行之間的位元組距離。如果為 0,則假設影像為緊密排列(行之間沒有填充)。如果省略,則步幅預設為 0。
- 方向
影像中的第一行是否為螢幕上的頂行 (1) 或底行 (-1)。 如果省略,則方向預設為 1。
原始模式欄位用於決定如何解壓縮資料以符合 PIL 的內部像素佈局。PIL 支援大量原始模式;如需完整清單,請參閱 Unpack.c
模組中的表格。下表描述了一些常用的 原始模式
模式 |
描述 |
---|---|
|
1 位元雙色調,儲存時最左邊的像素位於最高
有效位。0 表示黑色,1 表示白色。
|
|
1 位元反轉雙色調,儲存時最左邊的像素位於
最高有效位。0 表示白色,1 表示黑色。
|
|
1 位元反向雙色調,儲存時最左邊的像素位於
最低有效位。0 表示黑色,1 表示白色。
|
|
8 位元灰階。0 表示黑色,255 表示白色。 |
|
8 位元反轉灰階。0 表示白色,255 表示黑色。 |
|
8 位元調色盤對應影像。 |
|
24 位元真彩色,儲存為 (紅色、綠色、藍色)。 |
|
24 位元真彩色,儲存為 (藍色、綠色、紅色)。 |
|
24 位元真彩色,儲存為 (紅色、綠色、藍色、填充)。 填充
像素可能有所不同。
|
|
24 位元真彩色,行交錯 (首先是所有紅色像素,然後是
所有綠色像素,最後是所有藍色像素)。
|
請注意,對於最常見的情況,原始模式與模式相同。
Python 影像函式庫支援許多其他解碼器,包括 JPEG、PNG 和 PackBits。如需詳細資訊,請參閱 decode.c
原始碼檔案和函式庫隨附的標準外掛程式實作。
解碼浮點數資料¶
PIL 提供一些特殊的機制,允許您將各種格式載入到模式 F
(浮點數) 影像記憶體中。
您可以使用 raw
解碼器讀取資料以任何標準機器資料類型封裝的影像,使用下列原始模式之一
模式 |
描述 |
---|---|
|
32 位元原生浮點數。 |
|
8 位元無號整數。 |
|
8 位元帶號整數。 |
|
16 位元小端無號整數。 |
|
16 位元小端帶號整數。 |
|
16 位元大端無號整數。 |
|
16 位元大端帶號整數。 |
|
16 位元原生無號整數。 |
|
16 位元原生帶號整數。 |
|
32 位元小端無號整數。 |
|
32 位元小端帶號整數。 |
|
32 位元大端無號整數。 |
|
32 位元大端帶號整數。 |
|
32 位元原生無號整數。 |
|
32 位元原生帶號整數。 |
|
32 位元小端浮點數。 |
|
32 位元大端浮點數。 |
|
32 位元原生浮點數。 |
|
64 位元小端浮點數。 |
|
64 位元大端浮點數。 |
|
64 位元原生浮點數。 |
位元解碼器¶
如果原始解碼器無法處理您的格式,PIL 也提供特殊的「位元」解碼器,可用於將各種封裝格式讀入浮點數影像記憶體中。
若要將位元解碼器與 PIL.Image.frombytes()
函式搭配使用,請使用下列語法
image = Image.frombytes(
mode, size, data, "bit",
bits, pad, fill, sign, orientation
)
在圖塊描述項中使用時,參數欄位應該看起來像這樣
(bits, pad, fill, sign, orientation)
欄位的使用方式如下
- 位元
每個像素的位元數 (2-32)。沒有預設值。
- 填充
行之間的填充,以位元為單位。 如果沒有填充,則為 0,如果行填充至完整位元組,則為 8。如果省略,則填充值預設為 8。
- 填滿
控制如何將資料新增至解碼器位元緩衝區,以及如何從中儲存資料。
- 填滿=0
將位元組新增至解碼器緩衝區的 LSB 端;從 MSB 端儲存像素。
- 填滿=1
將位元組新增至解碼器緩衝區的 MSB 端;從 MSB 端儲存像素。
- 填滿=2
將位元組新增至解碼器緩衝區的 LSB 端;從 LSB 端儲存像素。
- 填滿=3
將位元組新增至解碼器緩衝區的 MSB 端;從 LSB 端儲存像素。
如果省略,則填滿順序預設為 0。
- 符號
如果非零,則位元欄位會進行符號擴充。如果為零或省略,則位元欄位為無號。
- 方向
影像中的第一行是否為螢幕上的頂行 (1) 或底行 (-1)。 如果省略,則方向預設為 1。
以 C 撰寫您自己的檔案編解碼器¶
檔案編解碼器的生命週期分為 3 個階段
設定:Pillow 會在解碼器或編碼器登錄中尋找函式,如果找不到則會回到內部核心影像物件上的名為
[編解碼器名稱]_decoder
或[編解碼器名稱]_encoder
的函式。將使用tile
中的args
元組呼叫該函式。轉換:會重複使用影像資料區塊呼叫編解碼器的
decode
或encode
函式。清除:如果編解碼器已登錄清除函式,則會在轉換程序結束時呼叫該函式,即使發生例外狀況亦然。
設定¶
目前的慣例是,編解碼器設定函式名為 PyImaging_[編解碼器名稱]DecoderNew
或 PyImaging_[編解碼器名稱]EncoderNew
,且定義於 decode.c
或 encode.c
中。其 Python 繫結名為 [編解碼器名稱]_decoder
或 [編解碼器名稱]_encoder
,並在函式陣列的編解碼器區段中,從 _imaging.c
檔案中設定。
設定函式需要呼叫 PyImaging_DecoderNew
或 PyImaging_EncoderNew
,而且至少要設定 decode
或 encode
函式指標。此物件中感興趣的欄位包括
- decode/encode
解碼或編碼函式的函式指標,其具有存取
im
、state
和要轉換的資料緩衝區的權限。- cleanup
清除函式的函式指標,具有存取
state
的權限。- im
目標影像,將由 Pillow 設定。
- state
ImagingCodecStateInstance,將由 Pillow 設定。
context
成員是不透明的結構,編解碼器可以使用此結構來儲存任何格式特定的狀態或選項。- pulls_fd/pushes_fd
如果解碼器的
pulls_fd
或編碼器的pushes_fd
設定為 1,則state->fd
將是指向類似 Python 檔案物件的指標。編解碼器可以使用codec_fd.c
中的函式,直接使用類似檔案的物件讀取或寫入,而不是透過緩衝區推送資料。在 3.3.0 版中新增。
轉換¶
使用目標 (核心) 影像、編解碼器狀態結構和要轉換的資料緩衝區呼叫解碼或編碼函式。
編解碼器有責任從緩衝區中提取盡可能多的資料,並傳回已使用的位元組數。下一次呼叫編解碼器將包含先前未使用的尾部。將在處理資料時多次呼叫編解碼器函式。
或者,如果設定 pulls_fd
或 pushes_fd
,則會使用空的緩衝區呼叫解碼或編碼函式一次。編解碼器有責任在該一次呼叫中轉換整個圖磚。使用此方法將提供編解碼器更大的自由度,但如果編解碼器一次將整個圖磚保留在記憶體中,則該自由度可能意味著記憶體使用量增加。
如果發生錯誤,請設定 state->errcode
並傳回 -1。
在成功時傳回 -1,而不設定錯誤碼。
清除¶
在編解碼器傳回負值或發生錯誤後,會呼叫清除函式。此函式應釋放任何已配置的記憶體,並釋放來自外部程式庫的任何資源。
以 Python 撰寫您自己的檔案編解碼器¶
Python 檔案解碼器和編碼器應分別衍生自 PIL.ImageFile.PyDecoder
和 PIL.ImageFile.PyEncoder
,並且至少應覆寫解碼或編碼方法。應使用 PIL.Image.register_decoder()
和 PIL.Image.register_encoder()
進行登錄。如同檔案編解碼器的 C 實作,Python 型檔案編解碼器的生命週期分為三個階段
設定:Pillow 會在解碼器或編碼器登錄中尋找編解碼器,然後將類別具現化。
轉換:會重複使用要解譯的資料緩衝區呼叫執行個體的
decode
方法,或者重複使用要輸出的資料大小呼叫encode
方法。或者,如果解碼器的
_pulls_fd
屬性(或編碼器的_pushes_fd
屬性)設定為True
,則decode
和encode
將只會被呼叫一次。在解碼器中,可以使用self.fd
來存取類似檔案的物件。使用此方法將會為編解碼器提供更多自由,但如果整個檔案一次性被編解碼器載入記憶體,則這種自由可能會導致記憶體使用量增加。在
decode
中,一旦資料被解譯,就可以使用set_as_raw
來填充影像。清理:當轉換完成後,會呼叫實例的
cleanup
方法。這可以用來清理編解碼器使用的任何資源。但是,如果您將
_pulls_fd
或_pushes_fd
設定為True
,那麼您可能選擇在decode
或encode
的末尾執行任何清理工作。
有關 PIL.ImageFile.PyDecoder
的範例,請參閱 DdsImagePlugin。有關同時使用 PIL.ImageFile.PyDecoder
和 PIL.ImageFile.PyEncoder
的外掛程式,請參閱 BlpImagePlugin