Help us understand the problem. What is going on with this article?

PIL/Pillowで画像の色を高速に置換する

More than 3 years have passed since last update.

PIL/Pillowで色を置換する場合、簡単に思いつくのはImage.getpixel/Image.putpixelを使う方法だ。

しかしながら、Image.getpixel/Image.putpixelは1枚の画像を処理するのに数秒かかってしまうほど遅い、非常に遅い。
numpyを使って高速に置換する方法もあるが、色の置換ごときに依存モジュールを増やすのも馬鹿らしい。

そこでシンプルにPIL/Pillowのみで高速に色を置換する方法を考えた。

サンプルとして以下の画像の服の色(255, 204, 0)を(48,255,48)に置換してみよう。

sample.png result.png

なお画像はRGB24ビットでアルファなし、コードはpython2で記述する。

画像を色ごとに分解する

まず、Image.splitを使い画像を各色ごとのバンドに分解する。

from __future__ import unicode_literals, print_function, absolute_import
from PIL import Image, ImageChops

img = Image.open("sample.png")

r, g, b = img.split()

各RGBバンドの内容は以下のようになる。

r.png g.png b.png

Image.pointで特定の色ごとに2値化する

src_color = (255, 204, 0)

_r = r.point(lambda _: 1 if _ == src_color[0] else 0, mode="1")
_g = g.point(lambda _: 1 if _ == src_color[1] else 0, mode="1")
_b = b.point(lambda _: 1 if _ == src_color[2] else 0, mode="1")

Image.pointはテーブル変換するメソッドである、今回はlambdaを使用して各バンドごとに特定の値のみ抜き出す画像を生成している。

ポイントは引数にmode="1"を指定しているところで、modeに"1"を指定すると色深度が1ビットの画像が生成される。
これは後に述べるImageChopsモジュールでの変換に必要な処理である。

各RGBバンドの内容は以下のようになる。

_r.png _g.png _b.png

各バンドをAND合成してマスクを生成する

ImageChopsモジュールは、加算合成や乗算合成など様々な合成処理を行える便利なモジュールである。

今回はImageChops.logical_andを使用してAND合成をする、このメソッドはmode="1"のImageしか受け付けない。

mask = ImageChops.logical_and(_r, _g)
mask = ImageChops.logical_and(mask, _b)

すべてのバンドで1になっているピクセル(つまり特定の色)を抜き出したマスクができる。

mask.png

あとはこのマスクを使用して置換したい色を塗ればOK。

dst_color = (48,255,48)
img.paste(Image.new("RGB", img.size, dst_color), mask=mask)

処理速度

getpixel/putpixelを使用した場合と処理を比べてみた。

In [27]: %time replace_put_pixel(img, src_color, dst_color)
Wall time: 234 ms

In [28]: %time replace_fast(img, src_color, dst_color)
Wall time: 1.96 ms

getpixel/putpixel版は234 msなのに対し、今回の方法は1.96 msとなった。
実に100倍以上の速さである。

e-zero1
映像制作の企画・開発、モバイルアプリの制作を行っています。
http://www.e-zero1.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした