From a4178c60fb93ab680e14c5597e054988169fbeb1 Mon Sep 17 00:00:00 2001 From: Steven Robertson Date: Sat, 14 Apr 2012 22:55:00 -0700 Subject: [PATCH] Update the genome specs a bit --- cuburn/filters.py | 5 +---- cuburn/genome/convert.py | 22 +++++++++---------- cuburn/genome/specs.py | 44 +++++++++++++++++++------------------- cuburn/genome/spectypes.py | 19 +++++++--------- cuburn/genome/use.py | 26 ++++++++++++---------- cuburn/render.py | 4 ++-- 6 files changed, 58 insertions(+), 62 deletions(-) diff --git a/cuburn/filters.py b/cuburn/filters.py index ebb15e2..8bf1d17 100644 --- a/cuburn/filters.py +++ b/cuburn/filters.py @@ -118,7 +118,4 @@ class ColorClip(Filter, ClsMod): filter_map = dict(bilateral=Bilateral, logscale=Logscale, haloclip=HaloClip, colorclip=ColorClip) def create(gprof): - # TODO: redesign this (should not have to care about internals of - # use.Wrapper in order to find types from TypedList elements) - filts = gprof._val.get('filters') or gprof.spec['filters'].defaults - return [filter_map[f['type']]() for f in filts] + return [filter_map[f]() for f in gprof.filter_order] diff --git a/cuburn/genome/convert.py b/cuburn/genome/convert.py index d5b57f0..ecde73f 100644 --- a/cuburn/genome/convert.py +++ b/cuburn/genome/convert.py @@ -105,9 +105,7 @@ def convert_xforms(flame): xfs.update(make_symm_xforms(float(flame['symmetry']), len(xfs))) return xfs -split_to_dict = lambda keys: lambda v: dict(zip(keys, map(float, v.split()))) -pair = split_to_dict('xy') -rgb_triple = split_to_dict('rgb') +pair = lambda v: dict(zip('xy', map(float, v.split()))) xform_structure = ( ('pre_affine', 'coefs', convert_affine), @@ -125,22 +123,20 @@ xform_structure = ( # (dst, cvt_dict) for properties that are built from multiple source keys. # If a function returns 'None', its key is dropped from the result. flame_structure = ( - ('info.author', 'nick', str), - ('info.author_url', 'url', lambda s: 'http://' + str(s)), - ('info.name', 'name', str), + ('author.name', 'nick', str), + ('author.url', 'url', lambda s: 'http://' + str(s)), + ('name', 'name', str), - ('camera.center', 'center', pair), - ('camera.rotation', 'rotate', float), - ('camera.dither_width', 'filter', float), + ('camera.center', 'center', pair), + ('camera.rotation', 'rotate', float), + ('camera.dither_width', 'filter', float), ('camera.scale', lambda d: float(d['scale']) / float(d['size'].split()[0])), - ('filters.colorclip.gamma', 'filter', float), + ('filters.colorclip.gamma', 'gamma', float), ('filters.colorclip.gamma_threshold', 'gamma_threshold', float), ('filters.colorclip.highlight_power', 'highlight_power', float), ('filters.colorclip.vibrance', 'vibrancy', float), - # Not sure about putting this one on colorclip - ('filters.colorclip.background', 'background', rgb_triple), ('filters.de.curve', 'estimator_curve', float), ('filters.de.radius', 'estimator_radius', float), @@ -149,6 +145,8 @@ flame_structure = ( float(d.get('estimator_radius', 11))) if 'estimator_minimum' in d else None), + ('filters.logscale.brightness', 'brightness', float), + ('palette', 'palette', util.palette_encode), ('xforms', convert_xforms), ('final_xform', 'finalxform', convert_xform), diff --git a/cuburn/genome/specs.py b/cuburn/genome/specs.py index 0e9ba34..fb136e8 100644 --- a/cuburn/genome/specs.py +++ b/cuburn/genome/specs.py @@ -18,12 +18,16 @@ xform = ( , 'variations': var_params }) -# Since the structure of the info element differs between anims, nodes and -# edges, we pull out some of the common elements here -author = String('Attribution in the form: "Name [][, url]"') -name = String('A human-readable name for this entity') -src = String('The identifier of the source node') -dst = String('The identifier of the destination node') +author = ( + { 'name': String("Human-readable name of author") + , 'user': String("Email or other unique identifier") + , 'url': String("Website or other link provided by author") + }) + +link = ( + { 'src': String("Origin node ID and temporal offset") + , 'dst': String("Destination node ID and temporal offset") + }) filters = ( { 'bilateral': @@ -39,7 +43,7 @@ filters = ( , 'colorclip': { 'gamma': scalespline(4) , 'gamma_threshold': spline(0.01, 0, 1) - , 'highlight_power': spline(-1, -1, 1) + , 'highlight_power': spline(-1, -1) , 'vibrance': scalespline() } , 'de': @@ -65,37 +69,33 @@ time = ( }) base = ( - { 'camera': camera + { 'name': String("Human-readable name of this work") + , 'camera': camera , 'filters': filters , 'palette': list_(Palette()) , 'xforms': map_(xform) , 'final_xform': xform }) -anim = dict(base) -anim.update(type='animation', time=time, - info=dict(authors=list_(author), name=name, src=src, dst=dst, - origin=string_())) - # TODO node = dict(base) -node.update(type='node', info=dict(author=author, author_url=string_(), - id=string_(), name=name)) +node.update(type='node', blend=blend, author=author) # TODO -edge = dict(anim) -edge.update(type='edge', - info=dict(author=author, id=string_(), src=src, dst=dst), +edge = dict(base) +edge.update(type='edge', author=author, blend=blend, link=link, time=time, xforms=dict(src=map_(xform), dst=map_(xform))) +anim = dict(base) +anim.update(type='animation', authors=list_(author), link=link, time=time) + +default_filters = ['bilateral', 'logscale', 'colorclip'] # Yeah, now I'm just messing around. prof_filters = dict([(fk, dict([(k, refscalar(1, '.'.join(['filters', fk, k]))) for k in fv])) for fk, fv in filters.items()]) # And here's a completely stupid hack to drag scale into the logscale filter prof_filters['logscale']['scale'] = refscalar(1, 'camera.scale') -default_filters = [{'type': k} for k in ['bilateral', 'logscale', 'colorclip']] - profile = ( { 'duration': RefScalar(30, 'time.duration', 'Base duration in seconds') , 'fps': Scalar(24, 'Frames per second') @@ -104,8 +104,8 @@ profile = ( , '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') - , 'filters': TypedList(prof_filters, default_filters, - 'Ordered list of filters to apply') + , 'filter_order': list_(enum(filters.keys()), default_filters) + , 'filters': prof_filters }) # Types recognized as independent units with a 'type' key diff --git a/cuburn/genome/spectypes.py b/cuburn/genome/spectypes.py index 39c0edc..37bea22 100644 --- a/cuburn/genome/spectypes.py +++ b/cuburn/genome/spectypes.py @@ -3,13 +3,8 @@ from collections import namedtuple Map = namedtuple('Map', 'type doc') map_ = lambda type, d=None: Map(type, d) -List = namedtuple('List', 'type doc') -list_ = lambda type, d=None: List(type, d) - -# A list as above, but where each element is a dict with a 'type' parameter -# corresponding to one of the specs listed in the 'types' dict on this spec. -TypedList = namedtuple('TypedList', 'types defaults doc') -typedlist = lambda types, defaults=[], d=None: TypedList(types, defaults, d) +List = namedtuple('List', 'type default doc') +list_ = lambda type, default=(), d=None: List(type, default, d) Spline = namedtuple('Spline', 'default min max interp period doc var') def spline(default=0, min=None, max=None, interp='linear', period=None, d=None): @@ -37,9 +32,11 @@ refscalar = lambda default, ref, d=None: RefScalar(default, ref, d) String = namedtuple('String', 'doc') def string_(d=None): return String(d) -Enum = namedtuple('Enum', 'choices doc') -def enum(choices, d=None): - """Enum helper. 'choices' is a space-separated string.""" - return Enum(choices.split(), d) +Enum = namedtuple('Enum', 'choices default doc') +def enum(choices, default=None, d=None): + """Enum helper. 'choices' is a list or a space-separated string.""" + if isinstance(choices, basestring): + choices = choices.split() + return Enum(choices, default, d) Palette = namedtuple('Palette', '') diff --git a/cuburn/genome/use.py b/cuburn/genome/use.py index d078166..bc45a5c 100644 --- a/cuburn/genome/use.py +++ b/cuburn/genome/use.py @@ -1,18 +1,26 @@ import numpy as np -from spectypes import Spline, Scalar, RefScalar, Map, List, TypedList +from spectypes import Enum, Spline, Scalar, RefScalar, Map, List from specs import toplevels class Wrapper(object): + """ + Weird reverse visitor. Traversals of the tree are normally done externally + (via property accessors, in a lot of cases). This class alters the + returned representation of the underlying genome according to the provided + spec without imposing flow control. + """ def __init__(self, val, spec=None, path=()): if spec is None: + assert val.get('type') in toplevels, 'Unrecognized dict type' spec = toplevels[val['type']] # plain 'val' would conflict with some variation property names self._val, self.spec, self.path = val, spec, path def wrap(self, name, spec, val): - # Oh, a visitor. How... pedestrian. path = self.path + (name,) + if isinstance(spec, Enum): + return self.wrap_enum(path, spec, val) if isinstance(spec, Spline): return self.wrap_spline(path, spec, val) elif isinstance(spec, Scalar): @@ -25,13 +33,14 @@ class Wrapper(object): return self.wrap_Map(path, spec, val) elif isinstance(spec, List): return self.wrap_List(path, spec, val) - elif isinstance(spec, TypedList): - return self.wrap_TypedList(path, spec, val) return self.wrap_default(path, spec, val) def wrap_default(self, path, spec, val): return val + def wrap_enum(self, path, spec, val): + return val or spec.default + def wrap_spline(self, path, spec, val): return val @@ -45,12 +54,8 @@ class Wrapper(object): return self.wrap_dict(path, spec, val) def wrap_List(self, path, spec, val): - return [self.wrap(spec.type, v) for v in val] - - def wrap_TypedList(self, path, spec, val): - val = val if val is not None else spec.defaults - return [self.wrap(path+(str(i),), spec.types[v['type']], v) - for i, v in enumerate(val)] + val = val if val is not None else spec.default + return [self.wrap(path, spec.type, v) for v in val] def get_spec(self, name): if isinstance(self.spec, Map): @@ -73,7 +78,6 @@ class Wrapper(object): def __getitem__(self, name): return getattr(self, str(name)) - class RefWrapper(Wrapper): """ Wrapper that handles RefScalars, as with profile objects. diff --git a/cuburn/render.py b/cuburn/render.py index 3052368..974932e 100644 --- a/cuburn/render.py +++ b/cuburn/render.py @@ -210,7 +210,6 @@ class Renderer(object): del self._modrefs[:] self._modrefs.append(self.mod) - # TODO: make these customizable self.filts = filters.create(gprof) self.out = output.PILOutput() @@ -359,7 +358,8 @@ class RenderManager(ClsMod): self._iter(rdr, gnm, gprof, dim, tc) if self.copy_evt: self.stream_a.wait_for_event(self.copy_evt) - for filt, params in zip(rdr.filts, gprof.filters): + for filt, name in zip(rdr.filts, gprof.filter_order): + params = getattr(gprof.filters, name) filt.apply(self.fb, gprof, params, dim, tc, self.stream_a) rdr.out.convert(self.fb, gprof, dim, self.stream_a) self.filt_evt = cuda.Event().record(self.stream_a)