Skip to content

Instantly share code, notes, and snippets.

@ge0rg
Last active October 29, 2025 10:33
Show Gist options
  • Select an option

  • Save ge0rg/5ffe9a5053ab88fd75587bd0af0f5163 to your computer and use it in GitHub Desktop.

Select an option

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
#!/usr/bin/env python3
import rawpy
from PIL import Image
from blendmodes.blend import blendLayers, BlendType
from argparse import ArgumentParser
import numpy
import re
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=0.7,
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(r'\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
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}...')
blended_img = load_img(f_first) # RGBA image
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
# Blend images
if args.darken:
blended_img = blendLayers(blended_img, foreground_img_raw,
BlendType.DARKEN)
else:
blended_img = blendLayers(blended_img, foreground_img_raw,
BlendType.LIGHTEN)
if not args.output:
# build name from extracted image number(s)
if id_first is not None and id_last is not None:
args.output = f'blended_{id_first}-{id_last}.jpg'
elif id_first is not None:
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.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