Skip to content

Instantly share code, notes, and snippets.

@tuokri
Created September 5, 2020 23:31
import os
from pathlib import Path
from PIL import Image
from PIL.ImageColor import getcolor
from PIL.ImageColor import getrgb
from PIL.ImageOps import grayscale
def image_tint(src_path, tint="#ffffff"):
src_img = Image.open(src_path)
if src_img.mode not in ["RGB", "RGBA"]:
raise TypeError("Unsupported source image mode: {}".format(src_img.mode))
src_img.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 = (tuple(map(lambda lr: int(lr * sr + 0.5), range(256))) +
tuple(map(lambda lg: int(lg * sg + 0.5), range(256))) +
tuple(map(lambda lb: int(lb * sb + 0.5), range(256))))
l = grayscale(src_img) # 8-bit luminosity version of whole image
if Image.getmodebands(src_img.mode) < 4:
merge_args = (src_img.mode, (l, l, l)) # for RGB verion of grayscale
else: # include copy of src image"s alpha layer
a = Image.new("L", src_img.size)
a.putdata(src_img.getdata(3))
merge_args = (src_img.mode, (l, l, l, a)) # for RGBA verion of grayscale
luts += tuple(range(256)) # for 1:1 mapping of copied alpha values
return Image.merge(*merge_args).point(luts)
def do_it(input_image_path, alpha):
print("tinting '{}'".format(input_image_path))
root, ext = os.path.splitext(input_image_path)
suffix = "_Zombie"
result_image_path = root + suffix + ext
print("creating '{}'".format(result_image_path))
result = image_tint(input_image_path, "#6ff542")
if os.path.exists(result_image_path): # delete any previous result file
os.remove(result_image_path)
result.putalpha(alpha)
result.save(result_image_path) # file name"s extension determines format
print("done")
if __name__ == "__main__":
_alpha = Image.open(r"J:\RS2Export\CHR_VN_VN_Heads\Texture2D\ALPHA.tga").split()[-1]
_images = Path(r"J:\RS2Export")
_images = _images.rglob("*Head*D.tga")
for i in _images:
do_it(str(i.absolute()), _alpha)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment