天天看點

PIL 學習Image 類ImageDraw子產品

參考資料: Python圖像處理庫:pillow

Image 類

Pillow 中最重要的類就是 Image,該類存在于同名的子產品中。可以通過以下幾種方式執行個體化:從檔案中讀取圖檔,處理其他圖檔得到,或者直接建立一個圖檔。

使用 Image 子產品中的

open

函數打開一張圖檔:

from PIL import Image           
im = Image.open('E:/Images/5a2e2075f331d.png')           
im           
PIL 學習Image 類ImageDraw子產品

如果打開成功,傳回一個 Image 對象,可以通過對象屬性檢查檔案内容:

print(im.format, im.size, im.mode)           
PNG (1920, 1080) RGBA           
  • format

    屬性定義了圖像的格式(

    PNG

    ,

    JPG

    None

    ),如果圖像不是從檔案打開的,那麼該屬性值為

    None

  • size

    屬性是一個二進制

    tuple

    ,表示圖像的寬和高(機關為像素

    px

    );
  • mode

    屬性為表示圖像的模式,常用的模式為:

    L

    (luminance)為灰階圖,

    RGB

    為真彩色,

    CMYK

    為 pre-press 圖像。官方說明- 圖像模式完整清單
  • palette

    : 僅當 mode 為 P 時有效,傳回 ImagePalette 執行個體
  • info

    : 以字典的形式傳回執行個體的資訊

如果檔案不能打開,則抛出

IOError

異常。

當有一個

Image

對象時,可以用

Image

類的各個方法進行處理和操作圖像:

讀寫圖檔

Pillow 庫支援相當多的圖檔格式。直接使用 Image 子產品中的

open()

函數讀取圖檔,而不必先處理圖檔的格式,Pillow 庫自動根據檔案決定格式。

Image 子產品中的

save()

函數可以儲存圖檔,除非你指定檔案格式,那麼檔案名中的擴充名用來指定檔案格式。

例子:轉換圖像格式的腳本(

jpg

轉為

png

格式)

im.save('E:/Images/5a2e2075f331d.png', 'jpeg')           
import os           
root = 'E:/Images/'           
for name in os.listdir(root):
    infile = root + name
    f, e = os.path.splitext(infile)   # f 變量是除擴充名以外的檔案名,e 變量是擴充名
    if os.path.isfile(infile):
        outfile = f +".png"  # 拼湊輸出檔案名
        print(f)
        if infile != outfile:   # 儲存的圖像格式跟原圖像格式不一樣
            try:
                Image.open(infile).save(outfile)  # 轉換圖像格式
            except IOError:
                print("Cannot convert", infile)  # 圖像無法打開,則處理異常           
E:/Images/5a2e206693e4d
E:/Images/5a2e2075f331d
E:/Images/psb
E:/Images/psb1
E:/Images/README
Cannot convert E:/Images/README.md
E:/Images/thIZ7ILLM5
E:/Images/thO3CXS0S3
E:/Images/water           

建立縮略圖

縮略圖是網絡開發或者圖像軟體預覽常用的一種基本技術,使用 Python 的 Pillow 圖像庫可以很友善地建立縮略圖。

Image 類的

thumbnail()

方法可以用來制作縮略圖。它接受一個二進制數組作為縮略圖的尺寸,然後将執行個體縮小到指定尺寸。

例子:生成

JPEG

縮略圖,大小是原圖像的四分之一

for name in os.listdir(root):
    infile = root + name
    if os.path.isfile(infile):
        outfile = os.path.splitext(infile)[0] + ".thumbnail" # 縮略圖檔案名+字尾
        if infile != outfile:
            try:
                im   = Image.open(infile) # 打開圖像
                x, y = im.size  # 擷取原圖像的大小(width、height)
                im.thumbnail((x//2, y//2)) # 縮略圖大小
                im.save(outfile, "JPEG") # 儲存為 JPEG 格式
            except IOError:
                print("cannot create thumbnail for", infile)           
cannot create thumbnail for E:/Images/5a2e206693e4d.png
cannot create thumbnail for E:/Images/5a2e2075f331d.png
cannot create thumbnail for E:/Images/README.md           

注意:Pillow 庫不會直接解碼或者加載圖像栅格資料。當你打開一個檔案,隻會讀取檔案頭資訊用來确定格式,顔色模式,大小等等,檔案的剩餘部分不會主動處理。這意味着打開一個圖像檔案的操作十分快速,跟圖像大小和壓縮方式無關。

圖像的剪切、粘貼與合并操作

Image 類包含很多操作圖像區域的方法。

裁剪子矩形

crop()

方法可以從圖像中提取一個子矩形選區,如下:

im = Image.open('E:/Images/5a2e2075f331d.png')
box = (80, 80, 300, 300)
region = im.crop(box)
region           
PIL 學習Image 類ImageDraw子產品

矩形選區區域由一個 \(4\) 元元組決定,元組資訊表示

(左,上,右,下)

的坐标。Pillow 庫以圖像左上角為坐标原點 \((0,0)\),機關是

px

是以,上述代碼是複制了一個 \(220 \times 220\) pixels 的矩形選區。

處理子圖,粘貼回原圖

region = region.transpose(Image.ROTATE_270)   # 旋轉180°
im.paste(region, box)
im           
PIL 學習Image 類ImageDraw子產品

transpose()

方法可以将圖檔左右颠倒、上下颠倒、旋轉 \(90°\)、旋轉 \(180°\) 或旋轉 \(270°\)。

paste()

方法則可以将一個 Image 執行個體粘貼到另一個 Image 執行個體上。

def roll(image, delta):
    "Roll an image sideways"

    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))  # 右邊矩形選區
    part1.load() 
    part2.load()
    image.paste(part2, (0, 0, xsize-delta, ysize)) # 原右邊圖形貼到左邊
    image.paste(part1, (xsize-delta, 0, xsize, ysize))  # 原左邊圖形貼到右邊

    return image


im = Image.open('E:/Images/5a2e2075f331d.png')
print(im.size)   # (356, 362)

roll(im,100).save('E:/Images/5a2e2075f331d.png','JPEG')           
(450, 675)           
im           
PIL 學習Image 類ImageDraw子產品

要注意的是,當你使用

crop()

方法來修改圖像檔案的時候,

load()

方法會首先被調用。這是由于修改是一個惰性操作。如果

load()

未被調用,那麼在

paste

使用前都不會執行修改這個操作。這暗示着

part1

會在首次修改

image

的時候被修改。

分離和合并顔色通道

對于多通道圖像,有時候處理時希望能夠分别對每個通道處理,處理完成後重新合成多通道,如下:

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

對于

split()

函數,如果是單通道的,則傳回其本身。否則,傳回各個通道。

幾何變換

Image 類包含了

resize()

rotate

方法來變換圖像。前者需要傳入一個表示新大小的元組,後者需要傳入旋轉的角度。

簡單的幾何變換

out = im.resize((128, 128))
out           
PIL 學習Image 類ImageDraw子產品
rout = out.rotate(45)         # 順時針角度表示
rout           
PIL 學習Image 類ImageDraw子產品

旋轉圖像

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°           

更通用的圖像變換方法可以使用

transform()

ImageDraw子產品

ImageDraw 子產品提供了

Draw

類,它能在

Image

執行個體上進行簡單的 2D 繪畫。你可以使用這個子產品來建立新圖像或者修飾現有圖像。

有關 PIL 的更進階繪圖庫,可以參考

aggdraw子產品

建立 Draw 類的執行個體

要在 Image 執行個體上繪制新的圖樣,首先要建立一個

Draw

類的執行個體。

這裡粗略介紹下 Draw 類中的基本繪畫操作函數(英文都是函數名):

  • 弦/弧/扇形:

    chord

    /

    arc

    pieslice

  • 橢圓:

    ellipse

  • 線段/多段線:

    line

  • 點:

    point

  • 多邊形:

    polygon

  • 矩形:

    rectangle

  • 文字:

    text

  • 文字大小:

    textsize

詳細的使用說明,請看官方文檔:

Draw 類的各函數使用說明

畫直線

Draw 類提供了

line(xy,options)

函數繪制直線。

其中

xy

表示坐标清單,它可以是任何包含 \(2\) 元組

[(x,y),…]

或者數字

[x,y,…]

的序列對象,至少包含兩個坐标:

  • [(x1, y1), (x2, y2), …]

    :包含若幹個元組的清單
  • [x1, y1, x2, y2, …]

    :按照順序包含坐标資訊的清單
  • [x1, y1, (x2, y2), …]

    :以上兩種情況的混合
  • ((x1, y1), (x2, y2), …)

    :包含若幹個元組的元組
  • (x1, y1, x2, y2, …)

    :按照順序包含坐标資訊的元組
  • (x1, y1, (x2, y2), …)

  • options

    可用的選項:
    • fill = (R,G,B)

      :指定線條顔色
    • width = integer

      :指定線條寬度,機關是px
from PIL import Image,ImageDraw
im = Image.open('E:/Images/5a2e2075f331d.png')
drawAvatar = ImageDraw.Draw(im)

xSize,ySize = im.size

# 三等分位置
drawAvatar.line([0, 0.33 * ySize, xSize, 0.33 * ySize],\
    fill = (255, 100, 0), width = 3)
# 左下角到中心點,右下角到中心點
drawAvatar.line([(0, ySize), (0.5 * xSize, 0.5 * ySize), (xSize, ySize)],\
    fill = (255, 0, 0), width = 3)

im.save('E:/Images/5a2e2075f331d.jpg')           

探尋有趣之事!

繼續閱讀