Created
January 5, 2023 20:42
-
-
Save ge0rg/5ffe9a5053ab88fd75587bd0af0f5163 to your computer and use it in GitHub Desktop.
Script to combine a set of images in "darken" or "lighten" mode, using PIL, rawpy, blend_modes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import rawpy | |
from PIL import Image | |
from blend_modes import darken_only, lighten_only | |
from argparse import ArgumentParser | |
import numpy | |
import re | |
import sys | |
RAW_FORMATS = ['SRW'] | |
parser = ArgumentParser() | |
parser.add_argument("filenames", metavar='FILE', nargs='+', | |
help="files to blend together") | |
parser.add_argument("-d", "--darken", | |
action="store_true", dest="darken", default=False, | |
help="darken instead of lighten images") | |
parser.add_argument("-g", "--gamma", dest="gamma", type=float, default=1.0, | |
help="raw import gamma") | |
parser.add_argument("-o", "--output", dest="output", default=None, | |
help="save result to OUTFILE", metavar="OUTFILE") | |
parser.add_argument("-q", "--quality", dest="quality", type=int, default=95, | |
help="jpeg file quality") | |
parser.add_argument("-s", "--silent", | |
action="store_false", dest="verbose", default=True, | |
help="don't print status messages to stdout") | |
args = parser.parse_args() | |
def extract_number(fn): | |
try: | |
return re.findall('\d+', fn)[-1] | |
except IndexError: | |
return None | |
def load_img(fn): | |
if fn.split('.')[-1] in RAW_FORMATS: | |
raw = rawpy.imread(fn) | |
data = raw.postprocess(gamma=(args.gamma, args.gamma), no_auto_bright=True) | |
image = Image.fromarray(data) | |
else: | |
image = Image.open(fn) | |
return image.convert('RGBA') | |
f_first = args.filenames[0] | |
id_first = extract_number(f_first) | |
id_last = None | |
# Import background image | |
if args.verbose: | |
print(f"Loading {f_first}...") | |
background_img_raw = load_img(f_first) # RGBA image | |
background_img = numpy.array(background_img_raw) # Inputs to blend_modes need to be numpy arrays. | |
merged_float = background_img.astype(float) # Inputs to blend_modes need to be floats. | |
for f_next in args.filenames[1:]: | |
id_last = extract_number(f_next) | |
if args.verbose: | |
print(f"Adding {f_next}...") | |
# Import foreground image | |
foreground_img_raw = load_img(f_next) # RGBA image | |
foreground_img = numpy.array(foreground_img_raw) # Inputs to blend_modes need to be numpy arrays. | |
foreground_img_float = foreground_img.astype(float) # Inputs to blend_modes need to be floats. | |
# Blend images | |
if args.darken: | |
merged_float = darken_only(merged_float, foreground_img_float, opacity=1.0) | |
else: | |
merged_float = lighten_only(merged_float, foreground_img_float, opacity=1.0) | |
# Convert blended image back into PIL image | |
blended_img = numpy.uint8(merged_float) # Image needs to be converted back to uint8 type for PIL handling. | |
blended_img_raw = Image.fromarray(blended_img) # Note that alpha channels are displayed in black by PIL by default. | |
# This behavior is difficult to change (although possible). | |
# If you have alpha channels in your images, then you should give | |
# OpenCV a try. | |
if not args.output: | |
# build name from extracted image number(s) | |
if id_first and id_last: | |
args.output = f"blended_{id_first}-{id_last}.jpg" | |
elif id_first: | |
args.output = f"blended_{id_first}.jpg" | |
else: | |
args.output = f"blended.jpg" | |
# Display blended image | |
if args.verbose: | |
print(f"Saving {args.output}...") | |
blended_img_raw.convert('RGB').save(args.output, quality=args.quality, optimize=True, progressive=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment