Python 細聊可以媲美 PS 的 PIL 圖片處理庫
1 . 前言
PIL 是 Python Image Library 的簡稱。
PIL 庫中提供了諸多用來處理圖片的模組,可以對圖片做類似於 PS(Photoshop) 的編輯。比如:改變影像大小、旋轉影像、影像格式轉換,轉換顏色通道,影像增強,直方圖處理,插值和濾波等等。
PIL 是第三方庫,使用之前需要先安裝。
pip install pillow
2. 顏色模式
繼續之前先解一個重要概念:顏色模式。
所謂顏色模式:指在電腦中如何模擬出現實世界中的各種顏色,或準確講是一種顏色生成演算法。
常用的顏色模式:
-
RGB: 基礎理論就是對圖片中的每一個像素點,按紅 (Red)、綠(Green)、藍(Blue)三色系的不同分量組合出現實世界中的顏色。也可以說,我們在電腦上中所看到的 RGB 圖片的顏色是由三色系分量組合而成。或稱其有 3 個顏色通道。
Tip: 我們在電腦中所看到的圖片大多數都是像素圖片,像素圖片的特點就是整張圖片由很多的像素點組成,每一個像素點有自己的顏色。
在 RGB 顏色模式中,電腦為紅色、綠色、藍色分別分配 8 bit 的空間,也意味著紅色、綠色、藍色各自的分量變化是在 0~255(8bit 二進位換算成十進位度的最大值)之間。
所以電腦使用 RGB 顏色模式最多可以模擬出 255X255X255 種顏色,這應該足夠多了,已經可以讓電腦顯示出多彩斑斕的現實世界。
Tip: RGBA 是 RGB 顏色模式的增加版。除了可以模擬顏色,還可以摸擬透明度。A 是 Alpha 的縮寫,這是可以理解為透過或透明度的意思。
-
CMYK: C:Cyan = 青色,又稱為『天藍色』或是『湛藍』;M:Magenta = 品紅色,又稱為『洋紅色』;Y:Yellow = 黃色;K:blacK=黑色。CMYK 往往用於模擬印刷製品顏色。多用於廣告設計。
可以使用 PIL 庫的 ImageColor 模組的 getColor( ) 方法獲取一個顏色的不同顏色分量值。
from PIL import ImageColor
red = ImageColor.getrgb("red")
print(red)
'''
輸出結果
(255, 0, 0)
'''
Tip: ImageColor 模組內部維護有一個字典。
getColor( ) 方法用使用者提供的顏色名字為鍵,在字典中查找到對應顏色,再轉換成元組後返回。
colormap = { "aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "aqua": "#00ffff", "aquamarine": "#7fffd4", "azure": "#f0ffff", "beige": "#f5f5dc", "bisque": "#ffe4c4", "black": "#000000", "blanchedalmond": "#ffebcd", …… }
3. 處理圖片
處理圖片,主要是使用 PIL 庫的 Image 模組(也是核心模組)。在處理圖片之前請提前準備 3 張圖片(2 張 jpg 格式的圖片,1 張 png 格式的圖片)。
Tip: 為了方便操作,把圖片存放到項目目錄中,載入圖片時使用相對路徑描述。
這是一張名為 back_,jpg 的圖片
這是一張名為 dog.jpg 的圖片
這是一張名為 guo_ke.png 的圖片
3.1 基本操作
-
打開圖片: 處理圖片之前,先要打開圖片,可以使用 Image 模組中的 open( ) 方法 打開。此方法返回一個 PIL.Image.Image 類型的對象。
Tip: 本文不刻意區分函數和方法的概念,統一稱為方法。
# 原型
def open(fp, mode="r", formats=None):
……
open( ) 方法特點: open( ) 方法不會立即載入圖片數據,只有在處理圖片或調用圖片的 load( ) 方法時才會載入。
from PIL import Image
# open( ) 方法具有懶(延遲)載入的特性
img = Image.open("back_.jpg")
#立即載入圖片數據
img.load()
open( ) 方法參數說明:
- fp: 設置要打開的圖片文件。可以是字元串描述的文件名稱、也可以是一個文件對象。
from PIL import Image
# mode 必須是 "rb"
file = open("dog.png", mode="rb")
#文件對象作為參數
img = Image.open(file)
- mode: 對圖片文件操作的模式,默認是 「r」 。如果設置,也只能是 “r”。
- formats: 列表或元組類型,指定文件的打開格式,默認由方法自己判斷。
- 圖片的常規屬性: 圖片對象有幾個常用的屬性。
-
mode: 圖片的顏色模式(RGB,CMYK,RGBA,L……)。不同顏色模式的圖片其通道數量會有差異。
-
format: 圖片的格式(PNG,JPEG,GIF……)。JPEG格式的圖片沒有 A 通道,所以 JPEG 圖片是沒有透明度資訊的。PNG 圖片有 A 通道,具有透明性。GIF 圖片格式有幀的概念,一張 GIF 圖片其實是由多張圖片組成的,每一張圖片為一幀。
-
size: 圖片大小。在處理圖片時,圖片大小用 2 元組表示。
from PIL import Image
img = Image.open("dog.jpg", mode="r")
print("圖片顏色模式:", img.mode)
print("圖片的格式:", img.format)
print("圖片的大小:", img.size)
'''
輸出結果
圖片顏色模式: RGB
圖片的格式: JPEG
圖片的大小: (852, 639)
'''
- 修改圖片的屬性
PIL.Image.Image 對象提供有方法用來修改圖片的的屬性,如改變大小、改變模式、改變格式。
修改圖片的大小: 可使用圖片對象的 resize( ) 方法修改圖片的大小。此方法的參數必須是 2元組類型。*,返回一個圖片副本。
Tip: 不是直接修改原圖片的大小(原圖片是以 r 模式打開的)。類似於按給定的大小在原圖片做插值運算後重新生成一張圖片。
# 返回一個新的 PIL.Image.Image 對象
img=img.resize((300,300))
修改的圖片的顏色模式: 可以使用圖片對象的 conver( ) 方法修改圖片模式 。可以取值範圍: 1、L、P、RGB、RGBA、CMYK、YCbCr、I、F。返回的也是一個圖片副本。
1 模式: 黑白圖片模式,每一個像素只有黑或白兩個顏色。
from PIL import Image
img = Image.open("dog.jpg", mode="r")
img1 = img.convert('1')
img.show()
L 模式: 生成灰度圖片,每一個像素的顏色會重新按 L = R * 299/1000 + G * 587/1000 + B * 114/1000 進行換算。
Tip: 當像素點的 RGB 顏色分量相同時,組合出來的就是灰色。灰色系有 256 階梯,從 0~255。0 表示黑色,255 表示白色,從0到255 顏色由黑逐漸轉向白。
(#000) 黑色,(#111、 # 222、 #333) ……逐漸到 (#FFF) 白色。灰度圖片要比黑白圖片的顏色層次豐富。
from PIL import Image
img = Image.open("dog.jpg", mode="r")
img = img.convert('L')
img.show()
當把 RGBA 模式轉換成 RGB 模式時,只會獲取其中的 RGB 顏色通道(捨棄透明資訊)。當由 RGB 模式轉換成 RGBA 模式時。會自動添加 A 通道,補值為 255。
from PIL import Image
img = Image.open("dog.jpg", mode="r")
print(img.mode)
print(img.getpixel((1, 1)))
img = img.convert("RGBA")
# 使用 getpixel( ) 方法獲取任意一點的顏色模式
print(img.getpixel((1, 1)))
print(img.mode)
'''
輸出結果
RGB
(207, 209, 222)
(207, 209, 222, 255)
RGBA
'''
- 保存圖片。圖片修改後可以使用 save( ) 方法對其保存。
def save(self, fp, format=None, **params):
save( ) 方法的參數說明:
-
fp: 字元串描述的文件名或一個文件對象。
-
format: 保存時指定圖片的格式(JPG、PNG……)。如果省略此參數,則由文件的擴展名確定。如果 fp 是一個文件對象,此參數不能省略。
-
params: 擴展參數。
保存圖片的副本:除了文件名不一樣,其它數據資訊都是相同。
from PIL import Image
img = Image.open("dog.jpg")
img.save("dog_01.jpg")
保存時修改圖片格式:
from PIL import Image
# 原圖片格式為 jpg
img = Image.open("dog.jpg")
print("dog.jpg圖片的顏色模式:", img.mode)
# 保存後圖片的格式為 png
img.save("dog_02.png")
img = Image.open("dog_02.png")
print("dog_02.png的顏色模式:", img.mode)
'''
輸出結果
dog.jpg圖片的顏色模式: RGB
dog_02.png的顏色模式: RGB
'''
Tip: dog.jpg 圖片是 RGB 模式,雖然在保存時指定 PNG 擴展名,但系統不會添加 A 通道, dog_02.png 圖片的顏色模式還是 RGB 模式。
如下程式碼會拋異常:因為無法將 RGBA 模式的圖片以 JPEG 格式保存
from PIL import Image
# png 格式的圖片有透明通道,其顏色模式是 RGBA.
img = Image.open("guo_ke.png")
print(img.mode)
# jpg 格式的圖片沒有透明通道
img.save("guo_ke.jpg")
'''
輸出結果
OSError: cannot write mode RGBA as JPEG
'''
3.2 變形操作
變形操作包括對圖片的旋轉、裁剪、複製和粘貼一系列操作。
rotate( )方法: 此方法使用一個角度值旋轉圖片,返回圖片副本。
角度為正值時以逆時針方向旋轉,負值向順時針方向旋轉.可以通過調整角度,讓圖片水平或垂直方向翻轉。
from PIL import Image
img = Image.open("dog.jpg")
img=img.rotate(30)
img.show()
默認情況下,圖片旋轉後的空白處填充黑色。可以使用 rotate( )方法 的 fillcolor 參數為圖片旋轉後留下的空白處指定填充顏色。
from PIL import Image
img = Image.open("dog.jpg")
img=img.rotate(-45,fillcolor="blue")
img.show()
可以使用 rotate( )方法 的 center 參數調整圖片的中心點的位置。
from PIL import Image
img = Image.open("dog.jpg")
img = img.rotate(45, fillcolor="blue", center=(20, 20))
img.show()
crop( ) 方法: 裁剪圖片,裁剪時需要在原圖片中確定一個需要保留的矩形區域。此方法返回一個圖片副本。
from PIL import Image
img = Image.open("dog.jpg")
# 4 元組,前 2 個數字 表示矩形的左上角,後 2 個數字 表示矩形的右下角
img = img.crop((0, 0, 400, 400))
img.show()
圖片的坐標系,圖片的最左上角為原點,水平向右為 X 正軸,垂直向下為 正軸.
裁剪後的圖片
copy( )方法: 複製圖片,返回和原圖片完全一樣的圖片副本。圖片副本和原圖片之間是完全獨立的,修改其中的一張圖片不會影響另一張圖片。
paste( )方法: 粘貼圖片,可以把一張圖片粘貼到另一張圖片上。如把小狗的圖片粘貼到背景圖片上。
def paste(self, im, box=None, mask=None):
參數說明:
- im: 需要被粘貼的圖片(這裡是小狗圖片)。
- box: 圖片被粘貼的位置。box 可以是一個 2 元組,這 2 個表示小狗圖片的左上角在背景圖片上的位置。如果是 4 元組,則被粘貼的圖片(小狗圖片)必須和 4 元組所描述的矩形區域的大小一樣。
- mask:遮罩圖片,遮罩圖片最好有透明性,如 PNG 圖片。
from PIL import Image
# 打開背景圖片
back_img = Image.open("back_.jpg")
# 打開小狗圖片
dog_img = Image.open("dog.jpg")
# 修改小狗的大小
dog_img = dog_img.resize((180, 120))
# 複製圖片
back_copy_img = back_img.copy()
# (300,200) 或(300,200,480,320)
back_copy_img.paste(dog_img, (300, 200))
back_copy_img.show()
遮罩圖片的使用,遮罩圖片只能是 “1”, “L” or “RGBA” 模式的圖片,且遮罩圖片和被粘貼的圖片大小必須一樣。
from PIL import Image
# 打開背景圖片
back_img = Image.open("back_.jpg")
# 打開小狗圖片
dog_img = Image.open("dog.jpg")
# 修改小狗的大小
dog_img = dog_img.resize((180, 120))
# 複製圖片
back_copy_img = back_img.copy()
# 遮罩圖片 需要有透明度資訊
mask_img = Image.open("js.png")
# 修改 遮罩圖片 和 小狗圖片一樣大小
mask_img = mask_img.resize((180, 120))
# 粘貼
back_copy_img.paste(dog_img, box=(300, 200, 480, 320), mask=mask_img)
back_copy_img.show()
3.3 合併顏色通道
RGB顏色模式的圖片有 3 個顏色通道,RGBA 顏色模式的圖片有 3 個顏色通道另加 1 個透明度通道。可以使用 split( ) 方法 分離出圖片的顏色通道,然後根據自己的需要再重新排列顏色通道得到不同效果的圖片。
分離小狗圖片的顏色通道,並重組顏色通道 。merge( ) 方法能通過指定的顏色通道創建一張新圖片。
from PIL import Image
dog_img = Image.open("dog.jpg")
# 分離顏色通道
r, g, b = dog_img.split()
# 打亂顏色通道得到新圖片 藍色通道和紅色通道互換
dog_img = Image.merge(dog_img.mode, (b, g, r))
dog_img.show()
可以對分離出來的顏色通道進行單獨修改修改,修改後再重組出新的圖片。
from PIL import Image
dog_img = Image.open("dog.jpg")
# 分離顏色通道
r, g, b = dog_img.split()
# 修改每一個顏色通道中的像素點的值
r = r.point(lambda i: i + 10)
g = r.point(lambda i: i * 0.6)
b = b.point(lambda i: i + 100)
dog_img = Image.merge(dog_img.mode, (r, g, b))
dog_img.show()
下面的程式碼使用 getpixel( ) 方法通過給定的坐標值找到每一點的顏色模式。然後對每一個像素點的顏色模式進行重構。
from PIL import Image
dog_img = Image.open("dog.jpg")
w, h = dog_img.size
for i in range(w):
for j in range(h):
# 獲取每一個像素點的顏色模式
c = dog_img.getpixel((i, j))
# 修改每一個像素點的顏色模式 紅色加重,綠色減少。
dog_img.putpixel((i, j), (c[0] + 100, c[1] - 20, c[2] + 30))
dog_img.show()
重組不同圖片中的顏色通道,要求所有圖片的大小都一樣。
from PIL import Image
dog_img = Image.open("dog.jpg")
# 分離顏色通道
r, g, b = dog_img.split()
img1 = Image.open("guo_ke.png")
#兩張圖片的大小一樣
img1=img1.resize(dog_img.size)
# js.png 顏色模式是 rgba
r1,g1,b1,a=img1.split()
# 混合 兩張圖片的不同通道
img=Image.merge("RGBA",(r1,g,b1,a))
img.show()
3.4 濾鏡和增加
可以像 PS 一樣給圖片添加濾鏡,並且可以調整圖片的對比度,色調……等資訊。
ImageFilter 模組提供有各種濾鏡對象,圖片使用濾鏡後會返回一個圖片副本。
-
BLUR:模糊效果
-
CONTOUR:輪廓效果
-
DETAIL:細節效果
-
EDGR_ENHANCE:邊界增強效果
-
EDGE_ENHANCE_MODE:閾值邊界增強效果
-
EMBOSS:浮雕效果
-
FIND_EDGES:邊界效果
-
SMOOTH:平滑效果
-
SMOOTH_MODE:閾值平滑效果
-
SHARPEN:銳化效果
給小狗圖片添加濾鏡效果:
from PIL import ImageFilter
from PIL import Image
dog_img = Image.open("dog.jpg")
# 浮雕效果
dog_img = dog_img.filter(ImageFilter.EMBOSS)
dog_img.show()
其它的效果大家可以自己試一試。
ImageEnhance 類提供有修改圖片對比度,色調等資訊的對象。這些對象都有一個 enhance(factor) 方法用來設置增強的強度。
-
Color(im):調整顏色平衡
-
Contrast(im):調整對比度
-
Brightness(im):調整亮度
-
Sharpness(im):調整銳度
增強圖片的高度:
from PIL import ImageFilter
from PIL import Image
from PIL import ImageEnhance
dog_img = Image.open("dog.jpg")
dog_img = dog_img.filter(ImageFilter.SHARPEN)
# 構建 Brightness 對象
dog_img=ImageEnhance.Brightness(dog_img)
# 調用 Brightness 對象的enhance()設置增加的數值
dog_img.enhance(2).show()
4 . 總結
本文介紹了 PIL 庫的 ImageColor、Image、ImageFilter、ImageEnchance 模組。除此之外,PIL 中還有很多與圖片處理相關的模組。使用這些模組,幾乎可以完成 PS 中能完成的任務。
對於圖片處理還有些高級應用,有時間再另開闢新文。