# This file is supposed to run inside Blender: # blender thefile.blend \ # --python /path/to/exr_sequence_to_jpeg.py \ # -- --exr-pattern /path/to/exr/files/prefix-*.exr import argparse import pathlib import re import sys import bpy # Find the EXR files to process. dashdash_index = sys.argv.index('--') parser = argparse.ArgumentParser() parser.add_argument('--exr-glob') parser.add_argument('--output-pattern') cli_args, _ = parser.parse_known_args(sys.argv[dashdash_index + 1:]) exr_glob = pathlib.Path(cli_args.exr_glob) imgdir = exr_glob.parent exr_files = sorted(imgdir.glob(exr_glob.name)) if not exr_files: raise ValueError(f'No files found for pattern {exr_glob}') # Create a copy of the scene without data, so we can fill the sequence editor # with an image sequence. bpy.ops.scene.new(type='EMPTY') scene = bpy.context.scene se = scene.sequence_editor_create() # Place files at the correct frame, based on their filename. # This makes the rendering consistent w.r.t. gaps in the frames. # This assumes the files are named '000020.exr' etc. min_frame = float('inf') max_frame = float('-inf') # Interpret the last continuous string of digits as frame number. frame_nr_re = re.compile(r'[0-9]+$') print(f'Loading {len(exr_files)} EXR files:') for file in exr_files: match = frame_nr_re.search(file.stem) if not match: raise ValueError(f'Unable to find frame number in filename {file.name}') frame_num = int(match.group(), 10) min_frame = min(min_frame, frame_num) max_frame = max(max_frame, frame_num) print(f' - {file} -> frame {frame_num}') se.sequences.new_image(file.name, str(file), 1, frame_num) print(f'Found files for frame range {min_frame}-{max_frame}') print() sys.stdout.flush() scene.frame_start = min_frame scene.frame_end = max_frame render = scene.render render.use_sequencer = True render.filepath = str(imgdir / cli_args.output_pattern) render.image_settings.file_format = 'JPEG' render.image_settings.quality = 90 render.use_overwrite = True # overwrite lesser quality previews bpy.ops.render.render(animation=True, use_viewport=False) bpy.ops.wm.quit_blender()