我在去年初為了還一本書整理了其中 Pillow 套件的用法, 參考 :
但最近在研究如何裁切 Matplotlib 所繪製的表格圖片時, 發現該書介紹的 Pillow 內容較少, 例如要用到的 crop() 方法就沒介紹, 所以將其重要用法重新整理, 參考書籍如下 :
# Python最強入門邁向數據科學之路:王者歸來(全彩印刷第二版) (深智) 第 17 章
# Python 自動化的樂趣 (碁峰 2017) 第 17 章
Pillow 官方文件參考 :
Pillow 套件在大量圖檔的處理自動化上非常有用.
一. PIL 套件中的常用模組 :
PIL 套件常用模組 | 說明 |
ImageColor | 色彩表示方式的轉換 |
Image | 圖片處理模組 |
ImageDraw | 繪圖模組 |
ImageFont | 字型模組 |
其中 Image 是主要的影像處理模組, ImageDraw 與 ImageColor 則是繪圖模組, 而 ImageColor 則是轉換色彩表示法的輔助模組.
二. ImageColor 模組用法 :
此模組主要用來做光影色彩表示方式的轉換, 可將下列 3 種顏色字串 (大小寫均可) 轉成 10 進位的色碼 tuple, 因為 Pillow 套件的圖片處理模組 Image 與繪圖模組 ImageDraw 中的物件方法會用到色碼元組當參數 :
- 16 進位色碼字串 : 例如 "#ff0000" 或 "#FF0000"
- 色彩名稱字串 : 例如 "RED" 或 'red'
- rgb() 函式字串 : 例如 'rgb(255, 0, 0)' 或 'rgb(100%, 0%, 0%)'
注意, 電腦程式都是使用 RGB 加色法模型 (additive color model), 因為螢幕是光的呈現, 而光是以加色法原理顯色的, 這有別於塗料或印刷所使用的減色法模型 (subtractive color model), 此模型使用 CMYK (青, 洋紅, 黃, 黑) 四種顏色來混出各種顏色.
首先用 Python 小工具 members.py 模組來查詢其公開成員, 關於 members 模組參考 :
將 members.py 下載到目前工作目錄後呼叫其 list_members() 函式即可得到其公開成員列表 :
>>> from PIL import ImageColor
>>> import members as mbr
>>> mbr.list_members(ImageColor)
Image <class 'module'>
colormap <class 'dict'>
getcolor <class 'function'>
getrgb <class 'function'>
re <class 'module'>
可見 PIL 套件的 ImageColor 模組有 5 個公開成員, 其中 Image 與 re 都是匯入的模組, colormap 是定義顏色名稱與其對應 16 進位色碼的字典 :
>>> from PIL import ImageColor
>>> ImageColor.colormap
{'aliceblue': '#f0f8ff', 'antiquewhite': '#faebd7', 'aqua': '#00ffff', .... (略)
而 getrgb() 與 getcolor() 則為顏色字串轉成 10 進位的 RGB 色碼 tuple, 兩個函式功能類似, 差別只是 getColor() 多了一個 mode 參數可選擇要傳回 RGB 還是 RGBA 的 tuple, 其中 A 是 0~255 的透明度數值, 0 為全透明 (這時不管 R, G, B 值為多少都看不到顏色), 255 為完全不透明, 這要 PNG 檔才有支援. 其語法如下 :
ImageColor.getrgb(color)
ImageColor.getcolor(color, mode)
其中 color 為上述的三種色彩描述字串, mode 為傳回值之模式字串, getcolor() 必須傳入這兩個參數, mode 可傳入 'RGB' (JPG 檔) 或 'RGBA' (PNG 檔), 前者傳回 (R, G, B) 元組, 後者傳回 (R, G, B, A) 元組. 顏色字串參數 color 不分大小寫, 即 'red' 或 'RED', '#ff0000' 或 '#FF0000' 皆可.
Pillow 的色彩名稱採用了 HTML 的色彩名稱, 參考 :
getrgb() 函式只會傳回 (R, G, B) 元組, 例如 :
>>> ImageColor.getrgb('#ff0000')
(255, 0, 0)
>>> ImageColor.getrgb('#0000FF')
(0, 0, 255)
>>> ImageColor.getrgb('yellow')
(255, 255, 0)
>>> ImageColor.getrgb('YELLOW')
(255, 255, 0)
>>> ImageColor.getrgb('rgb(255, 0, 0)')
(255, 0, 0)
>>> ImageColor.getrgb('RGB(255, 0, 0)')
(255, 0, 0)
getcolor() 則會根據 mode 傳回 RGB 或 RGBA 元組, 例如 :
>>> ImageColor.getcolor('#ff0000', 'RGBA')
(255, 0, 0, 255)
>>> ImageColor.getcolor('#ff0000', 'RGB')
(255, 0, 0)
>>> ImageColor.getcolor('yellow', 'RGB')
(255, 255, 0)
>>> ImageColor.getcolor('yellow', 'RGBA')
(255, 255, 0, 255)
注意, 'RGBA' 模式傳回的 A 都是 255 (不透明), 這樣 R, G, B 設定才會完全顯色.
三. Image 模組用法 :
Image 模組是 PIL 套件中的圖像處理主體, 教學文件參考 :
可用 members 模組的 list_members() 函式來檢視其公開成員 :
>>> import members as mbr
>>> mbr.list_members(Image)
ADAPTIVE <class 'int'>
AFFINE <class 'int'>
ANTIALIAS <class 'int'>
BICUBIC <class 'int'>
BILINEAR <class 'int'>
BOX <class 'int'>
CONTAINER <class 'int'>
CUBIC <class 'int'>
Callable <class 'abc.ABCMeta'>
DECODERS <class 'dict'>
DEFAULT_STRATEGY <class 'int'>
DecompressionBombError <class 'type'>
DecompressionBombWarning <class 'type'>
ENCODERS <class 'dict'>
EXTENSION <class 'dict'>
EXTENT <class 'int'>
Exif <class 'abc.ABCMeta'>
FASTOCTREE <class 'int'>
FILTERED <class 'int'>
FIXED <class 'int'>
FLIP_LEFT_RIGHT <class 'int'>
FLIP_TOP_BOTTOM <class 'int'>
FLOYDSTEINBERG <class 'int'>
HAMMING <class 'int'>
HUFFMAN_ONLY <class 'int'>
ID <class 'list'>
Image <class 'type'>
ImageMode <class 'module'>
ImagePointHandler <class 'type'>
ImageTransformHandler <class 'type'>
LANCZOS <class 'int'>
LIBIMAGEQUANT <class 'int'>
LINEAR <class 'int'>
MAXCOVERAGE <class 'int'>
MAX_IMAGE_PIXELS <class 'int'>
MEDIANCUT <class 'int'>
MESH <class 'int'>
MIME <class 'dict'>
MODES <class 'list'>
MutableMapping <class 'abc.ABCMeta'>
NEAREST <class 'int'>
NONE <class 'int'>
NORMAL <class 'int'>
OPEN <class 'dict'>
ORDERED <class 'int'>
PERSPECTIVE <class 'int'>
Path <class 'type'>
QUAD <class 'int'>
RASTERIZE <class 'int'>
RLE <class 'int'>
ROTATE_180 <class 'int'>
ROTATE_270 <class 'int'>
ROTATE_90 <class 'int'>
SAVE <class 'dict'>
SAVE_ALL <class 'dict'>
SEQUENCE <class 'int'>
TRANSPOSE <class 'int'>
TRANSVERSE <class 'int'>
TiffTags <class 'module'>
USE_CFFI_ACCESS <class 'bool'>
UnidentifiedImageError <class 'type'>
WEB <class 'int'>
alpha_composite <class 'function'>
atexit <class 'module'>
blend <class 'function'>
builtins <class 'module'>
cffi <class 'module'>
coerce_e <class 'function'>
composite <class 'function'>
core <class 'module'>
deferred_error <class 'type'>
effect_mandelbrot <class 'function'>
effect_noise <class 'function'>
eval <class 'function'>
fromarray <class 'function'>
frombuffer <class 'function'>
frombytes <class 'function'>
fromqimage <class 'function'>
fromqpixmap <class 'function'>
getmodebandnames <class 'function'>
getmodebands <class 'function'>
getmodebase <class 'function'>
getmodetype <class 'function'>
i32le <class 'function'>
init <class 'function'>
io <class 'module'>
isImageType <class 'function'>
isPath <class 'function'>
linear_gradient <class 'function'>
logger <class 'logging.Logger'>
logging <class 'module'>
math <class 'module'>
merge <class 'function'>
new <class 'function'>
numbers <class 'module'>
open <class 'function'>
os <class 'module'>
preinit <class 'function'>
radial_gradient <class 'function'>
register_decoder <class 'function'>
register_encoder <class 'function'>
register_extension <class 'function'>
register_extensions <class 'function'>
register_mime <class 'function'>
register_open <class 'function'>
register_save <class 'function'>
register_save_all <class 'function'>
registered_extensions <class 'function'>
struct <class 'module'>
sys <class 'module'>
tempfile <class 'module'>
warnings <class 'module'>
xml <class 'module'>
雖然成員眾多, 但實際上常用的函式是下面兩個 :
Image 模組常用函式 | 說明 |
new(mode, size, color=0) | 建立新的圖片物件, mode='RGB' 或 'RGBA', size=(w, h), 預設黑色 |
open(filename) | 開啟指定檔名之圖檔 |
new() 會依據傳入參數 mode 建立一個 Image 物件, 若要建立無透明度的 JPEG 檔須傳入 mode='RGB', 若要建立有透明度的 PNG 檔則傳入 mode='RGBA', 參數 size 為一個 (寬度, 高度) 畫素元組, 參數 color 可用色彩名例如 'yellow' 或色碼 '#ffff00' (不分大小寫), 如果沒有傳入 color, 預設是黑色背景 (color=0). 例如 :
>>> img=Image.new('RGB', (300, 200), 'yellow') # 300*200 px 黃底
>>> type(img)
<class 'PIL.Image.Image'>
此 Image 物件有一個 save() 方法可將物件存成圖片檔案, 例如 :
>>> img.save('test.jpg')
這時目前工作目錄下就會出現 300x200 黃底的 test.jpg 圖檔了 :
而 open() 函式則用來開啟已存在的圖檔, 以下範例使用我家的三隻小貓圖片 :
例如 :
>>> img=Image.open('kitten.jpg')
>>> type(img)
<class 'PIL.Image.Image'>
可見 open() 也是傳回一個 Image 物件. 然後呼叫此物件之 show() 方法會啟動預設看圖軟體顯示此圖片, 例如 :
>>> img.show()
結果如下 :
接下來進一步來檢視 Image 物件, 可呼叫 members 模組的 list_members() 函式並傳入上面已建立的 Image 物件即可檢視其有公開成員 :
>>> import members as mbr
>>> mbr.list_members(img)
alpha_composite <class 'method'>
category <class 'int'>
close <class 'method'>
convert <class 'method'>
copy <class 'method'>
crop <class 'method'>
draft <class 'method'>
effect_spread <class 'method'>
entropy <class 'method'>
filter <class 'method'>
format <class 'NoneType'>
format_description <class 'NoneType'>
frombytes <class 'method'>
getbands <class 'method'>
getbbox <class 'method'>
getchannel <class 'method'>
getcolors <class 'method'>
getdata <class 'method'>
getexif <class 'method'>
getextrema <class 'method'>
getim <class 'method'>
getpalette <class 'method'>
getpixel <class 'method'>
getprojection <class 'method'>
height <class 'int'>
histogram <class 'method'>
im <class 'ImagingCore'>
info <class 'dict'>
load <class 'method'>
mode <class 'str'>
palette <class 'NoneType'>
paste <class 'method'>
point <class 'method'>
putalpha <class 'method'>
putdata <class 'method'>
putpalette <class 'method'>
putpixel <class 'method'>
pyaccess <class 'NoneType'>
quantize <class 'method'>
readonly <class 'int'>
reduce <class 'method'>
remap_palette <class 'method'>
resize <class 'method'>
rotate <class 'method'>
save <class 'method'>
seek <class 'method'>
show <class 'method'>
size <class 'tuple'>
split <class 'method'>
tell <class 'method'>
thumbnail <class 'method'>
tobitmap <class 'method'>
tobytes <class 'method'>
toqimage <class 'method'>
toqpixmap <class 'method'>
transform <class 'method'>
transpose <class 'method'>
verify <class 'method'>
width <class 'int'>
Image 物件的常用屬性如下 :
Image 物件常用屬性 | 說明 |
width | 圖片寬度 (px) |
height | 圖片高度 (px) |
size | 圖片寬度與高度組成之 tuple (px) |
format | 圖片格式 (字串), 例如 'PNG', 'JPG' 等 |
format_description | 圖片格式的描述 (字串), 例如 'Potable network graphics' |
info | 圖片資訊字典, 包含 dpi 等訊息 |
mode | 圖片模式 (字串), 例如 'RGB' 或 'RGBN' |
Image 物件的常用方法如下 :
Image 物件常用方法 | 說明 |
save(filename) | 將物件存成圖檔 |
show() | 以作業系統預設看圖軟體顯示圖片物件 |
crop(box) | 剪裁圖片只留下 box 區域之像素, box 為 (左, 上, 右, 下) 座標元組 |
copy() | 複製圖片物件 (傳回新的 Image 物件) |
paste(image, (x, y)) | 在圖片物件左上座標 (x, y) 貼上 image 物件 (圖片會被更改) |
resize((w, h)) | 調整圖片物件為指定寬高尺寸 tuple (w, h) 後傳回新圖片物件 |
rotate(d [, expand=False]) | 將圖片逆時針旋轉 d 角度後傳回新圖片物件, expand=是否放大版面 |
transpose(method) | 將圖片做逕向翻轉後傳回新圖片物件, method=0 (左右), 1 (上下) |
getpixel((x, y)) | 取得座標 (x, y) 的像素值, 傳回 (R, G, B) 或 (R, G, B, A) 元組 |
putpixel((x, y), color) | 在座標 (x, y) 填入像素色彩值 color=(R, G, B) 或 (R, G, B, A) |
convert(mode) | 依據指定模式轉換圖片, 傳回新圖片物件. mode=1 轉成黑白圖片 |
split() | 將圖片中的 RGB 三原色分離為三個 Image 物件, 傳回 (R, G, B) 元組 |
merge(mode, bands) | 將單通道圖像 bands 以指定 mode 合併傳回新圖片物件 |
首先來測試 Image 物件的屬性, 例如 :
>>> img=Image.open('kitten.jpg')
>>> img.width
918
>>> img.height
446
>>> img.size
(918, 446)
>>> img.mode
'RGB'
>>> img.format
'JPEG'
>>> img.format_description
'JPEG (ISO 10918)'
>>> img.info
{'jfif': 257, 'jfif_version': (1, 1), 'dpi': (300, 300), 'jfif_unit': 1, 'jfif_density': (300, 300)}
接下來測試 Image 物件的方法, 其中 save() 與 show() 已在上面測試過不再重複.
1. 裁切圖片 :
呼叫 Image 物件的 crop(box) 方法即可依照傳入參數將指定區域 box 裁切出來成為新圖片物件傳回, 原物件不受影響. 參數 box 是 (x1, y1, x2, y2) 的座標區域, 其中 (x1, y1) 為左上角座標, (x2, y2) 為右下角座標. 注意, 裁切時不包含 (x2, y2) 這個像素點以及這一列與這一行之像素, 就像 range(1, 5) 不包含 5 一樣.
以上面三隻小貓圖片為例, 若要將最右邊那隻小貓的頭像裁切出來的話, 可先用小畫家或 Picpick 之類的圖片編輯軟體找出要裁切區域的上左下右座標, 然後組成要傳入 cro() 的 box 元組, 例如此處的欲裁切區域是 box=(532, 165, 747, 382) :
然後呼叫 crop() 方法進行裁切 :
>>> kitten_right=img.crop((532, 165, 747, 382))
>>> kitten_right.show()
結果如下 :
參考 :
2. 複製圖片 :
呼叫 Image 物件的 copy() 方法會傳回該物件的副本物件 (新物件), 例如 :
>>> img2=img.copy()
>>> img
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=918x446 at 0x219B41F5828>
>>> img2
<PIL.Image.Image image mode=RGB size=918x446 at 0x219B42083C8>
>>> img.size
(918, 446)
>>> img2.size
(918, 446)
可見 copy() 會傳回尺寸一樣的新 Image 物件, 呼叫 img2.show() 可知同樣是三隻小貓圖片.
3. 貼上圖片 :
呼叫 Image 物件的 paste(img, (x, y)) 方法會在圖片中指定的 (x, y) 左上座標處貼上傳入之另一個圖片物件. 注意, paste() 不會傳回新圖片, 而是直接更改原圖片物件. 例如可將上面裁切下來的最右邊小貓頭像 kitten_right 貼到圖片副本 img2 中 :
>>> img2.paste(kitten_right, (687, 13))
>>> img2.show()
結果如下 :
4. 更改圖片尺寸 :
呼叫 Image 物件的 resize((w, h)) 方法會調整圖片的尺寸為寬 w 高 h 的新圖片後傳回, 例如上面三隻小貓的圖片 img 尺寸為 (918, 446), 可用 resize() 將其縮小為長寬各一半 (即 1/4) :
>>> w, h=img.size
>>> w, h
(918, 446)
>>> img3=img.resize((int(w/2), int(h/2)))
>>> img3.size
(459, 223)
此處先用 size 屬性取得原圖 img 的寬與高 (w, h)=(918, 446), 然後呼叫 resize() 時將寬高都除以 2 以元組傳入, 傳回值為面積縮小為原圖 1/4 的新圖片物件, 尺寸為 (459, 223). 注意, resize((w, h)) 的傳入參數是 tuple (w, h), 不是 resize(w, h), 這樣會出現錯誤.
沒有留言 :
張貼留言