Python 圖像處理 Pillow 庫詳情

前言:

圖像處理是常用的技術,python 擁有豐富的第三方擴展庫,Pillow 是 Python3 最常用的圖像處理庫,目前最高版本5.2.0。Python2 使用Pil庫,兩者是使用方法差不多,區別在於類的引用不同。

註意:Pil 庫與 Pillow 不能同時存在與一個環境中,如果你已經安裝 Pil 庫,那麼請將他卸載。

使用 pip 安裝 Pillow:

> pip install Pillow

一、使用 Image.open() 創建圖像實例

Image Pillow 最常用的類,他可以通過多種方式創建圖像實例。

“ from PIL import Image ”導入 Image 模塊。然後通過 Image 類中的 open 函數即可載入圖像文件, open 函數會自動判斷圖片格式,隻需指定文件位置即可。成功,open 函數返回一個 Image 對象;載入文件失敗,則會引起 IOError 異常 。

1. 通過文件創建 Image 對象

通過文件創建 Image 圖像對象是最常用的方法

示例:通過文件創建 Image 圖像對象

from PIL import Image

image = Image.open('python-logo.png')  # 創建圖像實例
# 查看圖像實例的屬性
print(image.format, image.size, image.mode)

image.show() # 顯示圖像

代碼解讀:

實例屬性說明:
format 圖像格式
size 圖像的 (寬,高) 元組
mode 常見模式,默認 RGB 真彩圖像;L 為灰階圖像;CMYK 印刷色彩;RGBA 帶透明度的真彩圖像;YCbCr 彩色視頻格式;LAB L * a * b顏色空間;HSV 等。
show() 方法為使用系統默認圖片查看器顯示圖像,一般用於調試;

2. 從打開文件中讀取

可以從文件對象讀取而不是文件名,但文件對象必須實現 read( ) seek( )tell( ) 方法,並且是以二進制模式打開。

示例:從文件對象中讀取圖像

from PIL import Image
with open("hopper.ppm", "rb") as fp:
    im = Image.open(fp)


2. 從 string 二進制流中讀取

要從字符串數據中讀取圖像,需使用 io 類:

import io
from PIL import Image

im = Image.open(io.StringIO(buffer))


註意:在讀取圖像 header 之前將文件倒回(使用 seek(0) )。

3. 從tar文件中讀取

from PIL import TarIO

fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm")
im = Image.open(fp)

二、讀寫圖像

1. 格式轉換並保存圖像

Image 模塊中的 save 函數可以保存圖片,除非你指定文件格式,否則文件的擴展名就是文件格式。

import os
from PIL import Image

image_path='python-logo.png' # 圖片位置
f, e = os.path.splitext(image_path) # 獲取文件名與後綴
outfile = f + ".jpg"
if image_path != outfile:
    try:
        Image.open(image_path).save(outfile) # 修改文件格式
    except IOError:
        print("cannot convert", image_path)

註意: 如果你的圖片mode是RGBA那麼會出現異常,因為 RGBA 意思是紅色,綠色,藍色,Alpha 的色彩空間,Alpha 是指透明度。而 JPG 不支持透明度 ,所以要麼丟棄Alpha , 要麼保存為.png文件。解決方法將圖片格式轉換:

Image.open(image_path).convert("RGB").save(outfile)  # convert 轉換為 RGB 格式,丟棄Alpha


save() 函數有兩個參數,如果文件名沒有指定圖片格式,那麼第二個參數是必須的,他指定圖片的格式。

2. 創建縮略圖

創建縮略圖 使用 Image.thumbnail( size ), size 為縮略圖寬長元組。

示例: 創建縮略圖

import os
from PIL import Image

image_path = 'python-logo.png'  # 圖片位置
size = (128, 128)  # 文件大小
f, e = os.path.splitext(image_path)  # 獲取文件名與後綴
outfile = f + ".thumbnail"
if image_path != outfile:
    try:
        im = Image.open(image_path)
        im.thumbnail(size)  # 設置縮略圖大小
        im.save(outfile, "JPEG")
    except IOError:
        print("cannot convert", image_path)

註意: 出現異常,同上一個示例,convert(“RGB”)轉換圖片mode。

註意:除非必須,Pillow不會解碼或柵格數據。當你打開文件,Pillow通過文件頭確定文件格式,大小,mode等數據,餘下數據直到需要時才處理。這意味著打開文件非常快速,它與文件大小和壓縮格式無關。

三、剪貼,粘貼、合並圖像

Image類包含允許您操作圖像中的區域的方法。

如:要從圖像中復制子矩形圖像使用 crop() 方法。

1. 從圖像復制子矩形

示例: 截取矩形圖像

box = (100, 100, 400, 400)
region = im.crop(box)


定義box元組,表示圖像基於左上角為(0,0)的坐標,box 坐標為 (左,上,右,下)。註意,坐標是基於像素。示例中為 300 * 300 像素。

2. 處理子矩形並將其粘貼回來

示例: 在原圖上粘貼子矩形圖像

region = region.transpose(Image.ROTATE_180) # 顛倒180度
box = (400, 400, 700, 700)  # 粘貼位置,像素必須吻合,300 * 300
im.paste(region, box)


註意:將子圖(region) 粘貼(paste)回原圖時,粘貼位置 box 的像素與寬高必須吻合。而原圖和子圖的 mode 不需要匹配,Pillow會自動處理。

示例:滾動圖像

from PIL import Image


def roll(image, delta):
    """ 向側面滾動圖像 """
    xsize, ysize = image.size

    delta = delta % xsize
    if delta == 0: return image

    part1 = image.crop((0, 0, delta, ysize))
    part2 = image.crop((delta, 0, xsize, ysize))
    image.paste(part1, (xsize - delta, 0, xsize, ysize))
    image.paste(part2, (0, 0, xsize - delta, ysize))

    return image


if __name__ == '__main__':
    image_path = 'test.jpg'
    im = Image.open(image_path)
    roll(im, 300).show()  # 向側面滾動 300 像素

3. 分離和合並通道

Pillow 允許處理圖像的各個通道,例如RGB圖像有R、G、B三個通道。 split 方法分離圖像通道,如果圖像為單通道則返回圖像本身。merge 合並函數采用圖像的 mode 和 通道元組為參數,將它們合並成新圖像。

示例:交換RGB圖像的三個波段

r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))


註意:如果要處理單色系,可以先將圖片轉換為’RGB‘

四. 幾何變換

PIL.Image.Image 包含調整圖像大小 resize() 和旋轉 rotate() 的方法。前者采用元組給出新的大小,後者采用逆時針方向的角度。

示例:調整大小並逆時針旋轉 45度

out = im.resize((128, 128))
out = out.rotate(45)


要以90度為單位旋轉圖像,可以使用 rotate()transpose() 方法。後者也可用於圍繞其水平軸或垂直軸翻轉圖像。

示例:

out = im.transpose(Image.FLIP_LEFT_RIGHT) # 水平左右翻轉
out = im.transpose(Image.FLIP_TOP_BOTTOM) # 垂直上下翻轉
out = im.transpose(Image.ROTATE_90) # 逆時針90度
out = im.transpose(Image.ROTATE_180) # 逆時針180度
out = im.transpose(Image.ROTATE_270) # 逆時針270度


rotate() transpose() 方法相同,他們之間沒有差別, transpose() 方法比較通用。

五. 顏色變換

示例:mode 之間轉換

from PIL import Image
im = Image.open("hopper.ppm").convert("L") # 轉換為灰階圖像


註意:它支持每種模式轉換為”L” 或 “RGB”,要在其他模式之間進行轉換,必須先轉換模式(通常為“RGB”圖像)。

六. 圖像增強

1. Filters 過濾器

ImageFilter 模塊有很多預定義的增強過濾器,通過 filter() 方法運用。

示例:使用 filter()

from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)

2. 像素點處理

point() 方法可用於轉換圖像的像素值(如對比度),在大多數情況下,可以將函數對象作為參數傳遞格此方法,它根據函數返回值對每個像素進行處理。

示例:每個像素點擴大1.2倍

out = im.point(lambda i: i * 1.2)


上述方法可以用簡單的表達式進行圖像處理,還可以通過組合 point() paste() 對圖像的局部區域進行處理 。

3. 處理單獨通道

# 將通道分離
source = im.split()

R, G, B = 0, 1, 2

# 選擇紅色小於100的區域
mask = source[R].point(lambda i: i < 100 and 255)

# 處理綠色
out = source[G].point(lambda i: i * 0.7)

# 粘貼已處理的通道,紅色通道僅限於<100
source[G].paste(out, None, mask)

# 合並圖像
im = Image.merge(im.mode, source)


註意創建 mask 的語句:

imout = im.point(lambda i: expression and 255) 


對於 and 邏輯判斷來說,expression False (0) 已經能證明整個表達式為 False (0) , 否則還有對後面的結果進行判斷。所以 expression False (0) 返回 False (0) expression 為 True (本身的結果)是返回後面的 255;

同理對於 or 的邏輯判斷,當前面的表達式為 True,返回前面的值;當前面表達式為 False,返回後面表達式的值。

七、高級增強

其他圖像增強功能可以使用 ImageEnhance 模塊中的類。從圖像創建後,可以使用 ImageEnhance 快速調整圖片的對比度、亮度、飽和度和清晰度。

from PIL import ImageEnhance

enh = ImageEnhance.Contrast(im)  # 創建調整對比度對象
enh.enhance(1.3).show("增加30%對比度")


ImageEnhance 方法類型:

ImageEnhance.Contrast(im) 對比度
ImageEnhance.Color(im) 色彩飽和度
ImageEnhance.Brightness(im) 亮度
ImageEnhance.Sharpness(im) 清晰度

八、 動態圖像

Pillow 支持一些動態圖像處理(如FLI/FLC,GIF等格式)。TIFF文件同樣可以包含數幀圖像。

打開動態圖像時,PIL 會自動加載序列中的第一幀。你可以使用 seek tell 方法在不同的幀之間移動。

示例: 讀取動態圖像

from PIL import Image

im = Image.open("animation.gif")
im.seek(1) # 跳到第二幀

try:
    while 1:
        im.seek(im.tell()+1)  # tell() 獲取當前幀的索引號
except EOFError: # 當讀取到最後一幀時,Pillow拋出EOFError異常。
    pass # 結束


註意:有些版本的庫中的驅動程序僅允許您搜索下一幀。要回放文件,您可能需要重新打開它。都遇到無法回放的庫時,可以使用 for 語句循環實現。

示例for 使用 ImageSequence Iterator 類遍歷動態圖像

from PIL import ImageSequence
for frame in ImageSequence.Iterator(im):
    # ...處理過程...


示例:保存動態圖像

im.save(out, save_all=True, append_images=[im1, im2, ...])

參數說明:

out 需要保存到那個文件
save_all 為True,保存圖像的所有幀。否則,僅保存多幀圖像的第一幀。
append_images 需要附加為附加幀的圖像列表。列表中的每個圖像可以是單幀或多幀圖像( 目前隻有GIF,PDF,TIFF和WebP支持此功能)。

九、Postscript 打印

Pillow 允許通過 Postscript Printer 在圖片上添加圖像或文字。

from PIL import Image
from PIL import PSDraw

im = Image.open("test.jpg")
title = "hopper"
box = (1*72, 2*72, 7*72, 10*72) # in points

ps = PSDraw.PSDraw() # 默認 sys.stdout
ps.begin_document(title)

# 畫出圖像 (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)

# 畫出標題
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3*72, 4*72), title)

十、配置加載器 draft

某些解碼器允許在從文件中讀取圖像時對其進行操作。這通常可用於創建縮略圖時(當速度比質量更重要)加速解碼並打印到單色激光打印機(僅需灰階圖像時)。

draft() 方法操作已打開但尚未加載的圖像,使其盡可能匹配給定的模式和大小。它通過重新配置圖像解碼器來完成。僅適用於JPEG和MPO文件。

示例:使用 draft() 快速解碼圖像

from PIL import Image

im = Image.open('test.jpg')
print("original =", im.mode, im.size)

im.draft("L", (100, 100))
print("draft =", im.mode, im.size)


輸出:

original = RGB (1920, 1200)
draft = L (240, 150)


註意: 生成的圖像與請求的模式和大小可能不完全匹配。要確保圖像不大於給定大小,需改用縮略圖方法。

推薦閱讀: