最近はスマホで撮影した写真も驚くほど綺麗に撮れる。何気なく撮影した写真もとても綺麗だ。しかし、それが面白くなく感じることもある。Pythonで画像を加工するフィルタを作って、オリジナリティを演出してみよう。
Pythonで画像を扱うライブラリ「Pillow」とは
Pythonで画像を加工するには、Pillowライブラリが利用できる。Pillowを使うと画像の基本操作であるリサイズや部分抽出、形式変換が可能だ。そして、それに加えて、グレイスケールへの変換、明るさ・コントラスト・彩度の調整、ぼかしなどの効果を与えることもできる。そして、画像の各画素のピクセルに対しても何かしらの操作を行うこともできる。
Pillowパッケージをインストールするには、ターミナル(WindowsならPowerShell、macOSならターミナル.app)を起動して、次のコマンドを実行しよう。なお、Pillowのほかに、iPhoneで撮影した写真(HEIC形式)が読めるように「pillow-heif」や、簡単にダイアログを利用できるTkEasyGUIも一緒にインストールしよう。
pip install Pillow pillow-heif TkEasyGUI
iPhoneのHEIC形式の写真を読むには?
ちなみに、iPhoneで撮影した画像をPCに転送すると、「ファイル名.heic」のように「.heic」という拡張子になる(設定で変更が可能)。ところが、Pillowは、今のところライセンス上の問題と特許に関する懸念からHEIC形式をサポートしていない。そこで、別途「pillow-heif」をインストールし、下記のようにregister_heif_openerメソッドを呼ぶことで、HEIC形式の画像を読み込めるようになる。
from PIL import Image
from pillow_heif import register_heif_opener
# HEICが読めるように登録
register_heif_opener()
画像を加工するセピアフィルターを作ってみよう
それでは、画像を読み込んで、色褪せたセピアフィルターをかけて保存するプログラムを作ってみよう。下記のプログラムを「sepia.py」という名前で保存しよう。
from PIL import Image, ImageEnhance, ImageOps, ImageFilter
from pillow_heif import register_heif_opener
import TkEasyGUI as eg
# HEICが読めるように登録
register_heif_opener()
# 画像オブジェクトにセピアフィルタを適用する
def sepia_filter(image):
# コントラストと彩度を下げる
image = ImageEnhance.Contrast(image).enhance(0.9)
image = ImageEnhance.Color(image).enhance(0.4)
# セピアフィルタを適用する
gray = ImageOps.grayscale(image)
sepia = ImageOps.colorize(gray, "#442211", "#FFCCC1")
# ぼかし効果を加える
blur = sepia.filter(ImageFilter.GaussianBlur(radius=4))
return blur
if __name__ == "__main__":
# ファイル選択ダイアログを使う
filename = eg.popup_get_file()
if filename is not None:
# ファイルから画像を読む
image = Image.open(filename)
image = sepia_filter(image)
# フィルタ結果を保存
image.save(filename + "-output.jpg")
プログラムを実行するには、Pythonに同梱されているIDLEを使って、ファイルを読み込んで実行するか、ターミナルで下記のコマンドを実行しよう。
# Windowsの場合
python sepia.py
# macOSの場合
python3 sepia.py
プログラムを実行すると、ファイルの選択ダイアログが表示される。そこで、写真を選択すると、画像ファイルを読み込みセピア加工をして「ファイル名-output.jpg」という名前で出力する。
プログラムを見ると分かるのだが、関数sepia_filterでどのような処理を記述したかを確認してみよう。ImageEnhanceにあるコントラスト(Contrast)や彩度(Color)の機能を使ったり、ImageOpsにあるgrayscaleメソッドやcolorize関数を使う事で、画像をセピア加工することが可能になる。
特に、直接画像をセピア風にしているのが、ImageOps.colorize関数だ。この関数は、引数にグレイスケールの画像を指定するので、ImageOps.grayscale関数でカラー画像をグレイスケール(白黒256階調)に変換する。そして、ImageOps.colorize関数を使って、グレイスケールの黒の部分をこげ茶色(#442211)に、白の部分を茶色(#FFCCC1)に置換する。
セピア加工にノイズを足して風合いを演出しよう
セピア加工だけならば、簡単な画像ビューワーの補正ツールを使うだけでも実現できてしまう。せっかく自作フィルタを作るなら、もう少しオリジナリティを出してみよう。ここでは、ランダムにノイズを加えて風合いが出るようにしてみよう。
ここでは、画像に大量の細かいランダムなノイズを追加するadd_noise1関数と、黒い半透明の丸い染みを描画するadd_noise2関数を作ってみた。また、どちらの関数も仕上げにぼかしを適用して自然な仕上がりにしている。
# ランダムにピクセルの情報を変更する
def add_noise1(image):
width, height = image.size
pixels = image.load()
for _ in range(int(width * height * 0.02)): # 画像の2%のピクセルを変更
x = randint(0, width - 1)
y = randint(0, height - 1)
r, g, b = pixels[x, y]
noise = randint(-30, 30) # ノイズの範囲
pixels[x, y] = (
max(0, min(255, r + noise)),
max(0, min(255, g + noise)),
max(0, min(255, b + noise)),
)
# ぼかし効果を加える
image = image.filter(ImageFilter.GaussianBlur(radius=4))
return image
# 黒い染みをランダムに描画する
def add_noise2(image):
width, height = image.size
layer = Image.new("RGBA", image.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(layer)
for _ in range(int(width * height * 0.00001)):
x = randint(0, width)
y = randint(0, height)
size = randint(5, 35)
draw.ellipse((x, y, x + size, y + size), fill=(0, 0, 0, randint(50, 100)))
layer = layer.filter(ImageFilter.GaussianBlur(radius=4))
image.paste(layer, (0, 0), layer)
return image
なお、これらの関数を加えてプログラムを完成させたものを、こちらにアップした。
プログラムを実行すると、下記のようにセピア加工にランダムなノイズを重ねた画像を出力できる。
まとめ
以上、今回は画像フィルタを作成してみた。画像処理というと、難しい計算が必要になるのではと思いがちだが、Pillowパッケージを使う事で、さまざまなフィルタを利用できる。今回紹介したようにノイズを足すなど、簡単なアイデアで、オリジナルのフィルタを作成できる。本稿を参考に挑戦してみよう。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。これまで50冊以上の技術書を執筆した。直近では、「大規模言語モデルを使いこなすためのプロンプトエンジニアリングの教科書(マイナビ出版)」「Pythonでつくるデスクトップアプリ(ソシム)」「実践力を身につける Pythonの教科書 第2版」「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」など。