From 909643c3b4d28c715eecbbabe2e99d0cddf60c83 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Sun, 20 May 2012 13:03:27 -0700 Subject: [PATCH] cuburn.profile. --- cuburn/genome/specs.py | 12 +++++- cuburn/genome/use.py | 11 ----- cuburn/profile.py | 94 ++++++++++++++++++++++++++++++++++++++++++ main.py | 67 ++++-------------------------- 4 files changed, 112 insertions(+), 72 deletions(-) create mode 100644 cuburn/profile.py diff --git a/cuburn/genome/specs.py b/cuburn/genome/specs.py index 17354fb..3156fc4 100644 --- a/cuburn/genome/specs.py +++ b/cuburn/genome/specs.py @@ -109,13 +109,21 @@ prof_filters['logscale']['scale'] = refscalar(1, 'camera.scale') profile = ( { 'duration': RefScalar(30, 'time.duration', 'Base duration in seconds') , 'fps': Scalar(24, 'Frames per second') + , 'frame_width': refscalar(1, 'time.frame_width') + + , 'start': Scalar(None, 'First frame to render (1-indexed, inclusive)') + , 'end': Scalar(None, 'Last frame to render (1-indexed, exclusive; ' + 'negative indexes from the end)') + , 'skip': Scalar(0, 'Skip this many frames between each rendered frame') + , 'height': Scalar(1920, 'Output height in pixels') , 'width': Scalar(1080, 'Output width in pixels') - , 'frame_width': refscalar(1, 'time.frame_width') , 'spp': RefScalar(2000, 'camera.spp', 'Base samples per pixel') - , 'skip': Scalar(0, 'Skip this many frames between each rendered frame') + , 'filter_order': list_(enum(filters.keys()), default_filters) , 'filters': prof_filters + + , 'output_format': enum('jpg png tif', 'jpg') }) # Types recognized as independent units with a 'type' key diff --git a/cuburn/genome/use.py b/cuburn/genome/use.py index a053d95..63aec6b 100644 --- a/cuburn/genome/use.py +++ b/cuburn/genome/use.py @@ -199,14 +199,3 @@ class SplineEval(object): plt.xlim(0.0, 1.0) if show: plt.show() - -def wrap_genome(prof, gnm): - # It's not obvious that this is what needs to happen, so we wrap. The - # timing is simplistic, and may get expanded or moved later. - scale = gnm.get('time', {}).get('duration', 1) - gprof = RefWrapper(prof, toplevels['profile'], - other=SplineWrapper(gnm, scale=scale)) - nframes = round(gprof.fps * gprof.duration) - times = np.linspace(0, 1, nframes + 1) - times = times[:-1] + 0.5 * (times[1] - times[0]) - return gprof, times diff --git a/cuburn/profile.py b/cuburn/profile.py new file mode 100644 index 0000000..aa65aaa --- /dev/null +++ b/cuburn/profile.py @@ -0,0 +1,94 @@ +import numpy as np +import argparse + +from genome.specs import toplevels +from genome.use import RefWrapper, SplineWrapper + +BUILTIN={ + '1080p': dict(width=1920, height=1080), + '720p': dict(width=1280, height=720), + '540p': dict(width=960, height=540), + 'preview': dict(width=640, height=360, spp=1200, skip=1) +} + +def add_args(parser=None): + """ + Add profile argument groups to an ArgumentParser, for use with + get_from_args. (If `parser` is None, a new one will be made.) + """ + parser = argparse.ArgumentParser() if parser is None else parser + prof = parser.add_argument_group('Profile options') + prof.add_argument('--builtin-profile', '-P', choices=BUILTIN.keys(), + help='Set parameters below from a builtin profile. (default: 720p)', + default='720p') + prof.add_argument('--profile', '-p', type=argparse.FileType(), + metavar='PROFILE', help='Set profile from a JSON file.') + + tmp = parser.add_argument_group('Temporal options') + tmp.add_argument('--duration', type=float, metavar='TIME', + help="Override base duration in seconds") + tmp.add_argument('--fps', type=float, dest='fps', + help="Override frames per second") + tmp.add_argument('--start', metavar='FRAME_NO', type=int, + help="First frame to render (1-indexed, inclusive)") + tmp.add_argument('--end', metavar='FRAME_NO', type=int, + help="Last frame to render (1-indexed, exclusive, negative from end)") + tmp.add_argument('--skip', dest='skip', metavar='N', type=int, + help="Skip N frames between each rendered frame") + tmp.add_argument('--still', action='store_true', + help='Override start, end, and temporal frame width to render one ' + 'frame without motion blur.') + + spa = parser.add_argument_group('Spatial options') + spa.add_argument('--spp', type=int, metavar='SPP', + help="Set base samples per pixel") + spa.add_argument('--width', type=int, metavar='PX') + spa.add_argument('--height', type=int, metavar='PX') + + out = parser.add_argument_group('Output options') + out.add_argument('--codec', choices=['jpg', 'png', 'tiff']) + return parser + +def get_from_args(args): + """ + Get profile from an ArgumentParser result. Returns `(name, prof)`. + """ + if args.profile: + name = args.profile.name + base = json.load(args.profile) + else: + name = args.builtin_profile + base = BUILTIN[args.builtin_profile] + + if args.still: + base.update(frame_width=0, start=1, end=2) + for arg in 'duration fps start end skip spp width height'.split(): + if getattr(args, arg, None) is not None: + base[arg] = getattr(args, arg) + + return name, base + +def wrap(prof, gnm): + """ + Create a wrapped profile from plain dicts `prof` and `gnm`. The wrapped + profile follows the structure of the profile but returns genome-adjusted + data for any RefScalar value in its spec. + """ + scale = gnm.get('time', {}).get('duration', 1) + return RefWrapper(prof, toplevels['profile'], + other=SplineWrapper(gnm, scale=scale)) + +def enumerate_times(gprof): + """ + Given a profile, return a list of `(frame_no, center_time)` pairs. Note + that the enumeration is applied before `start`, `end`, and `skip`, and so + `frame_no` may be non-contiguous. + """ + nframes = round(gprof.fps * gprof.duration) + times = np.linspace(0, 1, nframes + 1) + times = list(enumerate(times[:-1] + 0.5 * (times[1] - times[0]), 1)) + if gprof.end is not None: + times = times[:gprof.end] + if gprof.start is not None: + times = times[gprof.start:] + return times[::gprof.skip+1] diff --git a/main.py b/main.py index 6815b71..c9b60d9 100755 --- a/main.py +++ b/main.py @@ -23,16 +23,9 @@ import numpy as np import pycuda.driver as cuda sys.path.insert(0, os.path.dirname(__file__)) -from cuburn import render, filters, output +from cuburn import render, filters, output, profile from cuburn.genome import convert, use, db -profiles = { - '1080p': dict(width=1920, height=1080), - '720p': dict(width=1280, height=720), - '540p': dict(width=960, height=540), - 'preview': dict(width=640, height=360, spp=1200, skip=1) -} - def save(out): # Temporary! TODO: fix this output.PILOutput.save(out.buf, out.idx) @@ -44,23 +37,20 @@ def main(args, prof): gdb = db.connect(args.genomedb) gnm, basename = gdb.get_anim(args.flame, args.half) - gprof, times = use.wrap_genome(prof, gnm) - rmgr = render.RenderManager() + gprof = profile.wrap(prof, gnm) basename += '_' if args.name is not None: basename = args.name prefix = os.path.join(args.dir, basename) frames = [('%s%05d%s.jpg' % (prefix, (i+1), args.suffix), t) - for i, t in enumerate(times)] - if args.end: - frames = frames[:args.end] - frames = frames[args.start::gprof.skip+1] + for i, t in profile.enumerate_times(gprof)] if args.resume: m = os.path.getmtime(args.flame) frames = (f for f in frames if not os.path.isfile(f[0]) or m > os.path.getmtime(f[0])) + rmgr = render.RenderManager() gen = rmgr.render(gnm, gprof, frames) if not args.gfx: @@ -110,8 +100,6 @@ def main(args, prof): label.text = '%s (%g fps)' % (out.idx, 1./real_dt) else: label.text += '.' - if args.sync: - cuda.Context.synchronize() pyglet.clock.set_fps_limit(30) pyglet.clock.schedule_interval(poll, 1/30.) @@ -138,49 +126,10 @@ if __name__ == "__main__": parser.add_argument('--genomedb', '-d', metavar='PATH', type=str, help="Path to genome database (file or directory, default '.')", default='.') - - parser.add_argument('--sync', action='store_true', dest='sync', - help='Use synchronous launches whenever possible') - - parser.add_argument('--duration', type=float, metavar='TIME', - help="Set base duration in seconds (30)", default=30) - parser.add_argument('--start', metavar='FRAME_NO', type=int, - default=0, help="First frame to render (inclusive)") - parser.add_argument('--end', metavar='FRAME_NO', type=int, - help="Last frame to render (exclusive, negative OK)") - - prof = parser.add_argument_group('Profile options') - prof.add_argument('-p', dest='prof', choices=profiles.keys(), - default='preview', help='Set profile, specifying defaults for all ' - 'options below. (default: "preview")') - prof.add_argument('--pfile', type=argparse.FileType(), metavar='PROFILE', - help='Set profile using a JSON file, overriding -p.') - prof.add_argument('--skip', dest='skip', metavar='N', type=int, - help="Skip N frames between each rendered frame") - prof.add_argument('--quality', type=int, metavar='SPP', - help="Set base samples per pixel") - prof.add_argument('--fps', type=float, dest='fps', - help="Set frames per second (24)") - prof.add_argument('--width', type=int, metavar='PX') - prof.add_argument('--height', type=int, metavar='PX') - - node = parser.add_argument_group('Node options') - node.add_argument('--half', action='store_true', - help='Use a half-loop when rendering a node.') - node.add_argument('--still', action='store_true', - help='Override start, end, and temporal frame width to render one ' - 'frame without motion blur. (Works on edges too)') + parser.add_argument('--half', action='store_true', + help='Use half-loops when converting nodes to animations') + profile.add_args(parser) args = parser.parse_args() - prof = dict(profiles[args.prof]) - if args.pfile: - prof = json.load(open(args.pfile)) - for k in ['duration', 'skip', 'quality', 'fps', 'width', 'height']: - if getattr(args, k) is not None: - prof[k] = getattr(args, k) - if args.still: - args.start = 0 - args.end = 1 - prof['frame_width'] = 0 - + pname, prof = profile.get_from_args(args) main(args, prof)