Python 讓我舅舅的書法作品和 PIL 庫發生點美的誤會

一枚大果殼發表於2022-03-15

Python 讓我舅舅的書法作品和 PIL 庫發生點美的誤會

1. 前言

不久之前寫過一篇文章,詳細介紹了 PIL 庫中的 Image 模組的使用。曾經學習過、使用過一段時間的 PS,認識 PIL 後,覺得這這玩意太好玩了,有了想使用 PIL 庫實現 PS 中的圖片特效的想法。

好,現在直接上案例,不另廢其它話。

2. 遮罩圖片

本文案例中所用的圖片素材,取自於我舅舅的書法作品(有點小名氣的書法家)。

第一張書法作品:心佛。

這張心中有佛的作品,我只需要上面的佛字,進行後續操作之前,首要任務是擷取佛字,也就整張圖片的上面一部分。

這裡使用兩種方案實現。

2.1 使用 Image 模組的裁剪方法

此方法簡單直接,裁剪時需要指定裁剪的矩形區域,左上角座標容易確定(0,0),右下角的座標這裡就大概判斷,眼觀一下,佛字大概是整幅作品的 四 分之一。

也可以稍精準的計算機出右下角的位置。

佛字和下面的內容之間有一條較完整的白色分割區域。可以從上向下以行為單位掃描整幅圖片,如果發現那一行畫素點的 R,G,B 的值近似相等且值都大於 200 以上,則可判斷出位置。有興趣者可以試試。

from PIL import Image
# 開啟原圖片
fo_img = Image.open("fo.jpg")
# 因後續要使用此圖片做遮罩,需要透明通道,所以要轉換成 RGBA 模式 
fo_img = fo_img.convert("RGBA")
# 獲取圖片本身大小
w, h = fo_img.size
# h/4-55 完全是試出來的偏差值 
fo_img = fo_img.crop((0, 0, w, h / 4 - 55))
fo_img.show()

如下是裁剪出來的圖片效果。

2.2 一個畫素點一個畫素點的裁剪

當把 RGB 模式轉換成 RGBA 模式後,Python 直譯器會給多出來的 a 通道賦值 255 。

因後面要使用這個佛字做遮罩。這裡需要把佛字圖片中的白色區域的 a 通道值修正為 0(白色區域全部變成透明區域)。

因是書法圖片,整張圖片整體上呈現明顯的黑白兩極分佈,白色區域的 R、G、B 分量值大概是在 200 左右,黑色文字的 R,G,B 顏色分量值大概在 100 以下。

Tip: 當使用一張圖片做遮罩時,圖片的 a 通道值為 0 的地方,被遮罩圖片所遮住的圖片會變成透明。a 通道為 255 的地方,表示完全不透明,從 0 到 255 之間由透明逐漸梯度變成不透明。

from PIL import Image
fo_img = Image.open("fo.jpg")
# 先轉換成 RGBA 模式
fo_img = fo_img.convert("RGBA")
# 獲取圖片本身大小
w, h = fo_img.size
# 建立一張空白的新圖片,大小和要裁剪的佛字圖片一樣大小
fo_only_img = Image.new(fo_img.mode, (w, int(h / 4) - 55))
w, h = fo_only_img.size
for i in range(w):
    for j in range(h):
        # 獲取每一畫素點的顏色分量
        r, g, b, a = fo_img.getpixel((i, j))
        # 把白色區域的 a 值修改為 0 ,白色區域的R,G,B值相近
        if r > 180 or g>180 or b>180:
            a = 0
        # 為新圖片指定新的顏色模式
        fo_only_img.putpixel((i, j), (r, g, b, a))
fo_only_img.show()

以上程式碼需注意,擷取出來的圖片資料被寫入一張新圖片中。

兩種方案比較:

  • 第一種方案提取後,還是需要再修改每一個畫素點的透明資訊。

  • 第 2 種方案一步到位。

處理完佛字圖片後,再準備一張春歸的書法作品做被遮罩圖片。

為了讓便於理解遮罩圖片與被遮罩圖片的關係,這裡畫一個示意圖。

在如下的程式碼還會建立一張做背景的白色圖片。

from PIL import Image
fo_img = Image.open("fo.jpg")
# 先轉換成 RGBA 模式
fo_img = fo_img.convert("RGBA")
# 獲取圖片本身大小
w, h = fo_img.size
# 建立一張空白的新圖片,大小和要裁剪的佛字圖片一樣大小
fo_only_img = Image.new(fo_img.mode, (w, int(h / 4) - 55))
w, h = fo_only_img.size
for i in range(w):
    for j in range(h):
        # 獲取每一畫素點的顏色分量
        r, g, b, a = fo_img.getpixel((i, j))
        # 把白色區域的 a 值修改為 0
        if r > 180:
            a = 0
        # 為新圖片指定新的顏色模式
        fo_only_img.putpixel((i, j), (r, g, b, a))
# 開始準備做遮罩效果之前,開啟被遮罩圖
chun_gui_img = Image.open("chungui.jpg")
# 修改 chun_gui_img 圖片和遮罩圖片一樣大小
chun_gui_img = chun_gui_img.resize(fo_only_img.size)
# 建立一張新的、空白的、純白色的背景圖片
new_img = Image.new("RGBA", chun_gui_img.size)
# 開始貼上
new_img.paste(chun_gui_img, mask=fo_only_img)
new_img.show()

執行程式碼後,可看到如下的圖片效果。這個效果在 PS 中更容易實現(畢竟人家是專業的圖片處理軟體)。

幾個變化:

  1. 反轉效果

    前面是把佛字圖片的白色區域的 a 值設定為 0,則白色區域所對應的春歸圖片會變成透明。現在反過來,把文字區域的 a 值設為 0。就可以看到和上圖相反的一個效果。

from PIL import Image
import random

fo_img = Image.open("fo.jpg")
# 先轉換成 RGBA 模式
fo_img = fo_img.convert("RGBA")
# 獲取圖片本身大小
w, h = fo_img.size
# 建立一張空白的新圖片,大小和要裁剪的佛字圖片一樣大小
fo_only_img = Image.new(fo_img.mode, (w, int(h / 4) - 55))
w, h = fo_only_img.size
# 步長值
step = 1
step1 = 1
for i in range(0, w, 1):
    for j in range(h):
        # 獲取每一畫素點的顏色分量
        r, g, b, a = fo_img.getpixel((i, j))
        # 文字區域的 a 值設定為 0
        if r < 80:
            a = 0
        # 為新圖片指定新的顏色模式
        fo_only_img.putpixel((i, j), (r, g, b, a))
# 開始準備做遮罩效果之前,先開啟底圖
chun_gui_img = Image.open("chungui.jpg")
# 修改 chun_gui_img 圖片和遮罩圖片一樣大小
chun_gui_img = chun_gui_img.resize(fo_only_img.size)
# 建立一張新的圖片
new_img = Image.new("RGBA", chun_gui_img.size,(100,200,80))
# 開始貼上
new_img.paste(chun_gui_img, mask=fo_only_img)
new_img.show()

如果把背景顏色設定為金色,佛字就會變成金色。同理,可以選擇任一喜歡的顏色。

new_img = Image.new("RGBA", chun_gui_img.size,ImageColor.getrgb("gold"))

  1. 顆粒效果

顆粒效果實現的思路和前面差不多,使用隨機模組讓文字區域的透明值隨機變化,讓文字區域有的地方透明,有的地方不透明,有的地方半透明。

from PIL import Image
import random
fo_img = Image.open("fo.jpg")
# 先轉換成 RGBA 模式
fo_img = fo_img.convert("RGBA")
# 獲取圖片本身大小
w, h = fo_img.size
# 建立一張空白的新圖片,大小和要裁剪的佛字圖片一樣大小
fo_only_img = Image.new(fo_img.mode, (w, int(h / 4) - 55))
w, h = fo_only_img.size
print(w)
# 步長值
step = 1
for i in range(0, w, 1):
    for j in range(h):
        # 獲取每一畫素點的顏色分量
        r, g, b, a = fo_img.getpixel((i, j))
        if r > 180:
            a = 0
        elif r < 100:
            # 隨機
            a -= random.randint(0, 255)
        # 為新圖片指定新的顏色模式
        fo_only_img.putpixel((i, j), (r, g, b, a))
# 開始準備做遮罩效果之前,先開啟底圖
chun_gui_img = Image.open("chungui.jpg")
# 修改 chun_gui_img 圖片和遮罩圖片一樣大小
chun_gui_img = chun_gui_img.resize(fo_only_img.size)
# 建立一張新的圖片
new_img = Image.new("RGBA", chun_gui_img.size)
# 開始貼上
new_img.paste(chun_gui_img, mask=fo_only_img)
new_img.show()

3. 字串圖片

把圖片中的每一畫素點用不同的字串替換,然後儲存字串資訊。如下程式碼中,白色區域的畫素點使用“仁”字替換。黑色文字區域的畫素點使用“佛”字替換。

from PIL import Image
import random
fo_img = Image.open("fo.jpg")
# 先轉換成 RGBA 模式
fo_img = fo_img.convert("RGBA")
# 獲取圖片本身大小
w, h = fo_img.size
# 建立一張空白的新圖片,大小和要裁剪的佛字圖片一樣大小
fo_only_img = Image.new(fo_img.mode, (w, int(h / 4) - 55))
w, h = fo_only_img.size
# 步長值
old_rgb = None
s = ''
for i in range(w):
    for j in range(h):
        # 獲取每一畫素點的顏色分量
        r, g, b, a = fo_img.getpixel((i, j))
        fo_only_img.putpixel((i, j), (r, g, b, a))

fo_only_img = fo_only_img.resize((300, 300))
w, h = fo_only_img.size
for i in range(h):
    for j in range(w):
        r, g, b, a = fo_only_img.getpixel((j, i))
        if r > 100:
            s += "仁"
        else:
            s += '佛'
    s += "\n"
with open("d:/fo.txt", 'w') as f:
    f.write(s)

找到對應文字檔案、開啟、縮小,可以清晰看到一個大大的“佛”字。如果放大,則會發現,整個佛字是由很多小佛字組成。

4. 總結

程式設計與書法一樣,都是一門藝術,藝術是為生活服務的。程式可以讓人類的生活更方便,書法則可以讓人類精神世界更美好。當兩者碰在一起後,世界充滿仁和愛。
獲取更多技術文章,請關注我的公眾號!

相關文章