source: trunk/synapse/synapse-render_video.py @ 267

Last change on this file since 267 was 267, checked in by sc, 11 years ago

synapse/synapse-render_video.py:

  • renderOverlayVideo added
  • support for cleaning up temporary image files added
  • Property svn:executable set to *
File size: 12.6 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright Puzzlebox Productions, LLC (2010)
5#
6# This code is released under the GNU Pulic License (GPL) version 2
7# For more information please refer to http://www.gnu.org/copyleft/gpl.html
8
9__changelog__ = """\
10Last Update: 2011.01.22
11"""
12
13#__doc__ = """\
14#Puzzlebox.Synapse.Protocol
15
16#usage:
17  #from Puzzlebox.Synapse import Protocol
18#"""
19
20# Example:
21# ./synapse-render_video.py \
22#       --file=/archive/BCI/Synapse/2011-01-13/2011-01-13\ sc-cod7-12-td-array.synapse \
23#       --directory=output \
24#       --prefix=2011-01-13\ sc-cod7-12-td-array \
25#       --background=blank-frame-720p.png
26
27#import Puzzlebox.Synapse.Interface as tgInterface
28
29import os, sys
30
31from mpl_toolkits.mplot3d import Axes3D
32from matplotlib.collections import PolyCollection
33from matplotlib.colors import colorConverter
34import matplotlib.pyplot as plt
35import numpy
36
37try:
38        import cPickle as pickle
39except:
40        import pickle
41
42#####################################################################
43# Globals
44#####################################################################
45
46DEBUG = 1
47
48DELETE_IMAGE_FILES_AFTER_COMPOSITE = True
49DELETE_IMAGE_FILES_AFTER_RENDER = True
50
51DEFAULT_INPUT_FILE = 'untitled.synapse'
52DEFAULT_HISTORY_IMAGE_FILE = 'history.png'
53DEFAULT_CURRENT_VALUES_IMAGE_FILE = 'current.png'
54DEFAULT_BLANK_FRAME_IMAGE_FILE = 'blank-frame-720p.png'
55
56DEFAULT_OUTPUT_DIRECTORY = 'output'
57DEFAULT_OUTPUT_IMAGE_PREFIX = 'output'
58
59DEFAULT_ATTENTION_COLOR = 'red'
60DEFAULT_MEDITATION_COLOR = 'blue'
61
62DEFAULT_HISTORY_WIDTH = 240
63DEFAULT_HISTORY_HEIGHT = 180
64
65DEFAULT_CURRENT_VALUES_WIDTH = 96
66DEFAULT_CURRENT_VALUES_HEIGHT = 720
67
68PATH_TO_COMPOSITE = '/usr/bin/composite'
69PATH_TO_MENCODER = '/usr/bin/mencoder'
70
71#####################################################################
72# Functions
73#####################################################################
74
75def renderCurrentValuesImage(x=None, y=None ,\
76                                  width = DEFAULT_CURRENT_VALUES_WIDTH, \
77                                  height = DEFAULT_CURRENT_VALUES_HEIGHT, \
78                                  x_color = DEFAULT_ATTENTION_COLOR, \
79                                  y_color = DEFAULT_MEDITATION_COLOR, \
80                                  output_file=DEFAULT_CURRENT_VALUES_IMAGE_FILE):
81       
82        figsize = (width / 100.0, height / 100.0) # @ 100 dpi
83       
84        fig = plt.figure(figsize=figsize)
85       
86        ax = fig.add_subplot(111, projection='3d')
87       
88        if x == None:
89                x = numpy.random.rand(1)[0] * 100
90       
91        if y == None:
92                y = numpy.random.rand(1)[0] * 100
93       
94        if type(x) != type([]):
95                x = [x]
96       
97        if type(y) != type([]):
98                y = [y]
99               
100        hist, xedges, yedges = numpy.histogram2d(x, y, bins=[2,1])
101
102        elements = (len(xedges) - 1) * (len(yedges) - 1)
103        xpos, ypos = numpy.meshgrid(xedges[:-1], yedges[:-1])
104
105        xpos = xpos.flatten()
106        ypos = ypos.flatten()
107        zpos = numpy.zeros(elements)
108        dx = 0.5 * numpy.ones_like(zpos)
109        dy = dx.copy()
110        dz = hist.flatten()
111
112        dx = [0.5]
113        dy = [0.5]
114        dz = [x, y]
115
116        ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=[x_color, y_color], zsort='average')
117
118        ax.set_axis_bgcolor('black')
119
120        ax.set_zlim3d(0, 100)
121
122        ax.view_init(10, -120)
123
124        plt.savefig(output_file, \
125                dpi=100, \
126                facecolor='black', \
127                edgecolor='black', \
128                format='png', \
129                pad_inches=0)
130       
131       
132        fig.clear()
133
134
135#####################################################################
136
137def renderHistoryImage(attention=None, \
138                            meditation=None ,\
139                            width = DEFAULT_HISTORY_WIDTH, \
140                            height = DEFAULT_HISTORY_HEIGHT, \
141                            attention_color = DEFAULT_ATTENTION_COLOR, \
142                            mediation_color = DEFAULT_MEDITATION_COLOR, \
143                            output_file=DEFAULT_HISTORY_IMAGE_FILE):
144       
145        figsize = (width / 100.0, height / 100.0) # @ 100 dpi
146
147        fig = plt.figure(figsize=figsize)
148
149        ax = fig.gca(projection='3d')
150
151        cc = lambda arg: colorConverter.to_rgba(arg, alpha=0.6)
152
153        xs = numpy.arange(-30, 0, 1)
154        verts = []
155        #zs = [0.333, 0.667]
156        #zs = [0.25, 0.75]
157        zs = [0.2, 0.8]
158
159
160        # Draw Attention History
161        if attention == None:
162                ys = numpy.random.rand(len(xs))
163
164                #for y in range(0, len(ys) - 1):
165                for y in range(0, len(ys)):
166                        ys[y] = ys[y] * 100 # bring random numbers up to the 100's range
167       
168        else:
169                ys = attention
170       
171        #ys[0], ys[-1] = 0, 0
172        #ys[0], ys[-1] = 50, 50
173        ys[0] = 0
174        #ys[1] = 50
175
176        verts.append(zip(xs, ys))
177
178
179        # Draw Meditation History
180        if meditation == None:
181                ys = numpy.random.rand(len(xs))
182
183                #for y in range(0, len(ys) - 1):
184                for y in range(0, len(ys)):
185                        ys[y] = ys[y] * 100 # bring random numbers up to the 100's range
186       
187        else:
188                ys = meditation
189       
190        #ys[0], ys[-1] = 0, 0
191        #ys[0], ys[-1] = 50, 50
192        ys[0] = 0
193        #ys[1] = 50
194
195        verts.append(zip(xs, ys))
196
197
198        poly = PolyCollection(verts, facecolors = [cc('red'), cc('blue')])
199        poly.set_alpha(0.8)
200        ax.add_collection3d(poly, zs=zs, zdir='y')
201
202        ax.set_axis_bgcolor('black')
203
204        ax.set_xlim3d(-30, 0)
205        ax.set_ylim3d(0, 1)
206        ax.set_zlim3d(0, 100)
207
208        plt.savefig(output_file, \
209                dpi=100, \
210                facecolor='black', \
211                edgecolor='black', \
212                format='png', \
213                pad_inches=0)
214       
215       
216        fig.clear()
217
218
219#####################################################################
220
221def openData(input_file=DEFAULT_INPUT_FILE):
222       
223        file = open(input_file, 'r')
224        data = pickle.load(file)
225        file.close()
226       
227        return(data)
228
229
230#####################################################################
231
232def mergeDataValues(data):
233       
234        merge = {}
235       
236        for signal in data['signals']:
237               
238                if 'eSense' in signal.keys():
239                       
240                        #index = int(signal['timestamp'])
241                        index = signal['timestamp']
242                       
243                        if index not in merge.keys():
244                                merge[index] = {}
245                       
246                        if 'attention' in signal['eSense'].keys():
247                                merge[ index ]['attention'] = \
248                                   signal['eSense']['attention']
249
250                        if 'meditation' in signal['eSense'].keys():
251                                merge[ index ]['meditation'] = \
252                                   signal['eSense']['meditation']
253
254
255        return(merge)
256
257
258#####################################################################
259
260def renderValuesFrames(data, \
261                            output_directory=DEFAULT_OUTPUT_DIRECTORY, \
262                            prefix=DEFAULT_OUTPUT_IMAGE_PREFIX):
263
264        data_keys = data.keys()
265        data_keys.sort()
266
267        counter = 1
268
269        for each in data_keys:
270               
271                if DEBUG > 1:
272                        print each,
273                        print data[each]
274               
275                index = int(each)
276               
277                output_image = "%s-values-%i.png" % (prefix, index)
278               
279                output_path = os.path.join( os.getcwd(), \
280                                            output_directory, \
281                                            output_image )
282               
283                if DEBUG:
284                        progress = int((float(counter) / len(data_keys)) * 100)
285                        print '%i%% [%i/%i] %s' % \
286                           (progress, counter, len(data_keys), output_image)
287               
288               
289                renderCurrentValuesImage(x=data[each]['attention'], \
290                                         y=data[each]['meditation'], \
291                                         output_file = output_path)
292               
293               
294                counter += 1
295
296
297#####################################################################
298
299def renderHistoryFrames(data, \
300                            output_directory=DEFAULT_OUTPUT_DIRECTORY, \
301                            prefix=DEFAULT_OUTPUT_IMAGE_PREFIX):
302
303        data_keys = data.keys()
304        data_keys.sort()
305
306        counter = 1
307       
308        attention = []
309        meditation = []
310       
311        for x in range(30):
312                attention.append(0)
313                meditation.append(0)
314
315        for each in data_keys:
316               
317                if DEBUG > 1:
318                        print each,
319                        print data[each]
320               
321                index = int(each)
322               
323                output_image = "%s-history-%i.png" % (prefix, index)
324               
325                output_path = os.path.join( os.getcwd(), \
326                                            output_directory, \
327                                            output_image )
328               
329                if DEBUG:
330                        progress = int((float(counter) / len(data_keys)) * 100)
331                        print '%i%% [%i/%i] %s' % \
332                           (progress, counter, len(data_keys), output_image)
333               
334                attention.append(data[each]['attention'])
335                attention = attention[1:]
336               
337                meditation.append(data[each]['meditation'])
338                meditation = meditation[1:]
339               
340                renderHistoryImage(attention=attention, \
341                                   meditation=meditation, \
342                                   output_file = output_path)
343               
344               
345                counter += 1
346
347
348#####################################################################
349
350def compositeFrames(data, \
351                         output_directory=DEFAULT_OUTPUT_DIRECTORY, \
352                         prefix=DEFAULT_OUTPUT_IMAGE_PREFIX, \
353                         background=DEFAULT_BLANK_FRAME_IMAGE_FILE):
354
355        composite_path = PATH_TO_COMPOSITE
356        #background_path = os.path.join( os.getcwd(), \
357                                            #output_directory, \
358                                            #background )
359        background_path = background
360
361        data_keys = data.keys()
362        data_keys.sort()
363
364        counter = 1
365
366        for each in data_keys:
367               
368                if DEBUG > 1:
369                        print each,
370                        print data[each]
371               
372                index = int(each)
373               
374                values_image = "%s-values-%i.png" % (prefix, index)
375                history_image = "%s-history-%i.png" % (prefix, index)
376                output_image = "%s-overlay-%i.png" % (prefix, index)
377               
378                values_image = convert_to_unix_filename(values_image)
379                history_image= convert_to_unix_filename(history_image)
380                output_image = convert_to_unix_filename(output_image)
381               
382                values_path = os.path.join( os.getcwd(), \
383                                            output_directory, \
384                                            values_image )
385                history_path = os.path.join( os.getcwd(), \
386                                            output_directory, \
387                                            history_image )
388                output_path = os.path.join( os.getcwd(), \
389                                            output_directory, \
390                                            output_image )
391               
392                if DEBUG:
393                        progress = int((float(counter) / len(data_keys)) * 100)
394                        print '%i%% [%i/%i] %s' % \
395                           (progress, counter, len(data_keys), output_image)
396               
397               
398                command = '%s %s -geometry +1184+0 %s %s' % \
399                              (composite_path, values_path, background_path, output_path)
400                os.system(command)
401               
402                command = '%s %s -geometry +1072-18 %s %s' % \
403                              (composite_path, history_path, output_path, output_path)
404                os.system(command)
405               
406               
407                if DELETE_IMAGE_FILES_AFTER_COMPOSITE:
408                        command = '/bin/rm %s' % values_path
409                        os.system(command)
410                        command = '/bin/rm %s' % history_path
411                        os.system(command)
412               
413               
414                counter += 1
415
416
417#####################################################################
418
419def renderOverlayVideo(output_directory=DEFAULT_OUTPUT_DIRECTORY, \
420                            prefix=DEFAULT_OUTPUT_IMAGE_PREFIX):
421       
422        prefix_path = convert_to_unix_filename(prefix)
423       
424        output_video = "%s-overlay.avi" % prefix
425       
426        output_video = convert_to_unix_filename(output_video)
427       
428        output_directory = os.path.join( os.getcwd(), \
429                                            output_directory)
430       
431        output_path = os.path.join( os.getcwd(), \
432                                            output_directory, \
433                                            output_video )
434       
435        command = \
436           '%s "mf://%s/%s-overlay*.png" -mf fps=1 -ovc lavc -lavcopts vcodec=ljpeg -o %s' % \
437              (PATH_TO_MENCODER, prefix_path, output_directory, output_path)
438
439        os.system(command)
440       
441       
442        if DELETE_IMAGE_FILES_AFTER_RENDER:
443                command = '/bin/rm %s/%s-overlay*.png' % (output_directory, prefix_path)
444                os.system(command)
445
446
447#####################################################################
448
449def convert_to_unix_filename(input_filename):
450
451        # This function will take in a filename as a string
452        # and output a properly-escaped unix filename, for shell processing
453
454        import string
455
456        output = ''
457
458        if (string.find(input_filename, '\\') == -1):
459                output = input_filename.replace('\n', '')
460                output = output.replace('\\', '\\\\')
461                output = output.replace(' ', '\\ ')
462                output = output.replace('(', '\\(')
463                output = output.replace(')', '\\)')
464                output = output.replace("'", "\\'")
465                output = output.replace('"', '\\"')
466                #output = output.replace(',', '\\,')
467                output = output.replace('&', '\\&')
468                output = output.replace('?', '\\?')
469                output = output.replace('%', '\\%')
470                output = output.replace('!', '\\!')
471                output = output.replace('/', '\\/')
472                output = output.replace(':', '\:')
473                #output = output.replace('>>', '\\>\\>')
474                output = output.replace('<', '\\<')
475                output = output.replace('>', '\\>')
476
477        return(output)
478
479
480#####################################################################
481# Main
482#####################################################################
483
484if __name__ == '__main__':
485
486        input_file = DEFAULT_INPUT_FILE
487        output_directory = DEFAULT_OUTPUT_DIRECTORY
488        prefix = DEFAULT_OUTPUT_IMAGE_PREFIX
489        background = DEFAULT_BLANK_FRAME_IMAGE_FILE
490
491        for each in sys.argv:
492                if each.startswith("--file="):
493                        input_file = each[ len("--file="): ]
494                if each.startswith("--directory="):
495                        output_directory = each[ len("--directory="): ]
496                if each.startswith("--prefix="):
497                        prefix = each[ len("--prefix="): ]
498                if each.startswith("--background="):
499                        background = each[ len("--background="): ]
500       
501        data = openData(input_file)
502       
503        data = mergeDataValues(data)
504
505        renderValuesFrames(data, output_directory, prefix)
506        renderHistoryFrames(data, output_directory, prefix)
507       
508        compositeFrames(data, output_directory, prefix, background)
509
510        renderOverlayVideo(output_directory, prefix)
511
Note: See TracBrowser for help on using the repository browser.