注:この回答のPILバージョンのPython3/枕フォークがここにあります。
更新4:私の答えに対する以前の更新は結局最後のものではなかったと思います。それを排他的に使用するように変換することは大きな改善でしたが、能力PIL
さえあれば、より良い、より厄介ではない方法があるべきであるように思われることがいくつかありました。PIL
さて、ドキュメントといくつかのソースコードをよく読んだ後、私がやりたいことが実際に可能であることに気づきました。トレードオフとして、手動で使用するルックアップテーブルを作成する必要があるため、コード全体が少し長くなります。ただし、その結果Image.point()
、3回ではなく、比較的遅いメソッドを1回呼び出すだけで済みます。
from PIL import Image
from PIL.ImageColor import getcolor, getrgb
from PIL.ImageOps import grayscale
def image_tint(src, tint='#ffffff'):
if Image.isStringType(src): # file path?
src = Image.open(src)
if src.mode not in ['RGB', 'RGBA']:
raise TypeError('Unsupported source image mode: {}'.format(src.mode))
src.load()
tr, tg, tb = getrgb(tint)
tl = getcolor(tint, "L") # tint color's overall luminosity
if not tl: tl = 1 # avoid division by zero
tl = float(tl) # compute luminosity preserving tint factors
sr, sg, sb = map(lambda tv: tv/tl, (tr, tg, tb)) # per component adjustments
# create look-up tables to map luminosity to adjusted tint
# (using floating-point math only to compute table)
luts = (map(lambda lr: int(lr*sr + 0.5), range(256)) +
map(lambda lg: int(lg*sg + 0.5), range(256)) +
map(lambda lb: int(lb*sb + 0.5), range(256)))
l = grayscale(src) # 8-bit luminosity version of whole image
if Image.getmodebands(src.mode) < 4:
merge_args = (src.mode, (l, l, l)) # for RGB verion of grayscale
else: # include copy of src image's alpha layer
a = Image.new("L", src.size)
a.putdata(src.getdata(3))
merge_args = (src.mode, (l, l, l, a)) # for RGBA verion of grayscale
luts += range(256) # for 1:1 mapping of copied alpha values
return Image.merge(*merge_args).point(luts)
if __name__ == '__main__':
import os
input_image_path = 'image1.png'
print 'tinting "{}"'.format(input_image_path)
root, ext = os.path.splitext(input_image_path)
result_image_path = root+'_result'+ext
print 'creating "{}"'.format(result_image_path)
result = image_tint(input_image_path, '#33b5e5')
if os.path.exists(result_image_path): # delete any previous result file
os.remove(result_image_path)
result.save(result_image_path) # file name's extension determines format
print 'done'
これは、左側に入力画像、右側に対応する出力を示すスクリーンショットです。上の行はアルファレイヤーのあるもので、下の行はアルファレイヤーのない同様の行です。