Last active
October 29, 2025 10:33
-
-
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 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