Another incompatible update to the genome format

This commit is contained in:
Steven Robertson 2011-12-17 09:23:39 -05:00
parent ed885534d6
commit c80b8a07a7
5 changed files with 102 additions and 38 deletions

View File

@ -11,7 +11,7 @@ _CODE = '''
#include<math_constants.h>
__global__
void colorclip(float4 *pixbuf, float gamma, float vibrancy, float highpow,
void colorclip(float4 *pixbuf, float gamma, float vibrance, float highpow,
float linrange, float lingam, float3 bkgd,
int fbsize, int blend_background_color) {
int i = threadIdx.x + blockDim.x * (blockIdx.x + gridDim.x * blockIdx.y);
@ -42,7 +42,7 @@ void colorclip(float4 *pixbuf, float gamma, float vibrancy, float highpow,
return;
}
float ls = vibrancy * alpha / pix.w;
float ls = vibrance * alpha / pix.w;
alpha = fminf(1.0f, fmaxf(0.0f, alpha));
float maxc = fmaxf(pix.x, fmaxf(pix.y, pix.z));
@ -71,9 +71,9 @@ void colorclip(float4 *pixbuf, float gamma, float vibrancy, float highpow,
}
}
pix.x += (1.0f - vibrancy) * powf(opix.x, gamma);
pix.y += (1.0f - vibrancy) * powf(opix.y, gamma);
pix.z += (1.0f - vibrancy) * powf(opix.z, gamma);
pix.x += (1.0f - vibrance) * powf(opix.x, gamma);
pix.y += (1.0f - vibrance) * powf(opix.y, gamma);
pix.z += (1.0f - vibrance) * powf(opix.z, gamma);
pix.x += (1.0f - alpha) * bkgd.x;
pix.y += (1.0f - alpha) * bkgd.y;
@ -318,7 +318,7 @@ class Filtering(object):
# TODO: implement integration over cubic splines?
gam = f32(1 / gnm.color.gamma(tc))
vib = f32(gnm.color.vibrancy(tc))
vib = f32(gnm.color.vibrance(tc))
hipow = f32(gnm.color.highlight_power(tc))
lin = f32(gnm.color.gamma_threshold(tc))
lingam = f32(lin ** (gam-1.0) if lin > 0 else 0)

BIN
cuburn/code/primes.bin Normal file

Binary file not shown.

View File

@ -7,6 +7,8 @@ import tempita
def crep(s):
"""Escape for PTX assembly"""
if isinstance(s, unicode):
s = s.encode('utf-8')
return '"%s"' % s.encode("string_escape")
class Template(tempita.Template):

View File

@ -1,7 +1,8 @@
import json
import base64
import numpy as np
from code.util import crep
class SplEval(object):
_mat = np.matrix([[1.,-2, 1, 0], [2,-3, 0, 1],
[1,-1, 0, 0], [-2, 3, 0, 0]])
@ -80,14 +81,12 @@ class SplEval(object):
return list(self.knots.T.flat)
class Palette(object):
"""Wafer-thin wrapper around palettes. For the future!"""
def __init__(self, datastr, fmt='rgb8'):
if fmt != 'rgb8':
def __init__(self, datastrs):
if datastrs[0] != 'rgb8':
raise NotImplementedError
if len(datastr) != 768:
raise ValueError("Unsupported palette width")
self.width = 256
pal = np.reshape(np.fromstring(datastr, np.uint8), (256, 3))
raw = base64.b64decode(''.join(datastrs[1:]))
pal = np.reshape(np.fromstring(raw, np.uint8), (256, 3))
self.data = np.ones((256, 4), np.float32)
self.data[:,:3] = pal / 255.0
@ -106,36 +105,61 @@ class _AttrDict(dict):
return dct
class Genome(_AttrDict):
"""
Load a genome description, wrapping all data structures in _AttrDicts,
converting lists of numbers to splines, and deriving some values. Derived
values are stored as instance properties, rather than replacing the
original values, such that JSON-encoding this structure should always
print a valid genome.
"""
# For now, we base the Genome class on an _AttrDict, letting its structure
# be defined implicitly by the way it is used in device code. More formal
# unpacking will happen soon.
def __init__(self, gnm, base_den):
# be defined implicitly by the way it is used in device code, except for
# these derived properties.
def __init__(self, gnm):
super(Genome, self).__init__(gnm)
for k, v in self.items():
if not isinstance(v, dict):
continue
v = _AttrDict(v)
# These two properties must be handled separately
if k not in ('info', 'time'):
_AttrDict._wrap(v)
self[k] = v
# TODO: this is a hack, figure out how to solve it more elegantly
self.spp = SplEval(self.camera.density.knotlist)
self.spp.knots[1] *= base_den
# TODO: decide how to handle palettes. For now, it's the caller's
# responsibility to replace this list with actual palettes.
pal = self.color.palette
self.decoded_palettes = map(Palette, self.palettes)
print self.color
pal = self.color.palette_times
if isinstance(pal, basestring):
self.color.palette = [(0.0, pal), (1.0, pal)]
elif isinstance(pal, list):
self.color.palette = zip(pal[::2], pal[1::2])
self.palette_times = [(0.0, int(pal)), (1.0, int(pal))]
else:
self.palette_times = zip(pal[::2], map(int, pal[1::2]))
# TODO: caller also needs to call set_timing()
self.adj_frame_width = None
self.canonical_right = (not self.get('link') or not self.link == 'self'
or not self.link.get('right'))
self.adj_frame_width, self.spp = None, None
self.canonical_right = not (self.get('link') and
(self.link == 'self' or self.link.get('right')))
def set_timing(self, base_dur, fps, offset=0.0, err_spread=True):
def set_profile(self, prof, offset=0.0, err_spread=True):
"""
Set frame timing. Must be called at least once prior to rendering.
Sets the instance props which are dependent on a profile. Also
calculates timing information, which is returned instead of being
attached to the genome. May be called multiple times to set different
options.
``prof`` is a profile dictionary. ``offset`` is the time in seconds
that the first frame's effective presentation time should be offset
from the natural presentation time. ``err_spread`` will spread the
rounding error in this frame across all frames, such that PTS+(1/FPS)
is exactly equal to the requested duration.
Returns ``(err, times)``, where ``err`` is the rounding error in
seconds (taking ``offset`` into account), and ``times`` is a list of
the central time of each frame in the animation in relative-time
coordinates. Also sets the ``spp`` and ``adj_frame_width`` properties.
"""
self.spp = SplEval(self.camera.density.knotlist)
self.spp.knots[1] *= prof['quality']
fps, base_dur = prof['fps'], prof['duration']
# TODO: test!
dur = self.time.duration
if isinstance(dur, basestring):
@ -162,3 +186,43 @@ class Genome(_AttrDict):
epts = np.linspace(-2*np.pi, 2*np.pi, nframes)
times = times + 0.5 * err * (np.tanh(epts) + 1)
return err, times
def json_encode_genome(obj, indent=0):
"""
Encode an object into JSON notation. This serializer only works on the
subset of JSON used in genomes.
"""
# TODO: test, like so many other things
isnum = lambda v: isinstance(v, (float, int, np.number))
def wrap(pairs, delims):
do, dc = delims
i = ' ' * indent
out = ''.join([do, ', '.join(pairs), dc])
if '\n' not in out and len(out) + indent < 70:
return out
return ''.join(['\n', i, do, ' ', ('\n'+i+', ').join(pairs),
'\n', i, dc])
if isinstance(obj, dict):
if not obj:
return '{}'
ks, vs = zip(*sorted(obj.items()))
if ks == ('b', 'g', 'r'):
ks, vs = reversed(ks), reversed(vs)
ks = [crep('%.8g' % k if isnum(k) else str(k)) for k in ks]
vs = [json_encode_genome(v, indent+2) for v in vs]
return wrap(['%s: %s' % p for p in zip(ks, vs)], '{}')
elif isinstance(obj, list):
vs = [json_encode_genome(v, indent+2) for v in obj]
if vs and len(vs) % 2 == 0 and isnum(obj[0]):
vs = map(', '.join, zip(vs[::2], vs[1::2]))
return wrap(vs, '[]')
elif isinstance(obj, SplEval):
return json_encode_genome(obj.knotlist, indent)
elif isinstance(obj, basestring):
return crep(obj)
elif isnum(obj):
return '%.8g' % obj
raise TypeError("Don't know how to serialize %s of type %s" %
(obj, type(obj)))

View File

@ -250,15 +250,13 @@ class Renderer(object):
info_size = 4 * len(self._iter.packer) * ntemporal_samples
d_infos = cuda.mem_alloc(info_size)
pals = genome.color.palette
if isinstance(pals, basestring):
pals = [0.0, pals, 1.0, pals]
ptimes, pidxs = zip(*genome.palette_times)
palint_times = np.empty(len(genome_times[0]), f32)
palint_times.fill(100.0)
palint_times[:len(pals)] = [p[0] for p in pals]
palint_times.fill(1e10)
palint_times[:len(ptimes)] = ptimes
d_palint_times = cuda.to_device(palint_times)
d_palint_vals = cuda.to_device(
np.concatenate([p[1].data for p in pals]))
pvals = [genome.decoded_palettes[i].data for i in pidxs]
d_palint_vals = cuda.to_device(np.concatenate(pvals))
if self.acc_mode in ('deferred', 'atomic'):
palette_fun = self.mod.get_function("interp_palette_hsv_flat")