2015-03-22 16:15:29 -06:00
|
|
|
from PIL import Image
|
|
|
|
from colour import Color
|
|
|
|
import numpy as np
|
2015-03-26 22:49:22 -06:00
|
|
|
import itertools as it
|
2015-03-22 16:15:29 -06:00
|
|
|
import warnings
|
|
|
|
import time
|
|
|
|
import os
|
|
|
|
import copy
|
2015-12-31 09:25:36 -08:00
|
|
|
from tqdm import tqdm as ProgressDisplay
|
2015-10-09 19:53:38 -07:00
|
|
|
import inspect
|
2016-02-23 22:29:32 -08:00
|
|
|
import subprocess as sp
|
2015-03-22 16:15:29 -06:00
|
|
|
|
|
|
|
from helpers import *
|
2015-10-27 21:00:50 -07:00
|
|
|
|
2016-02-23 22:29:32 -08:00
|
|
|
from camera import Camera
|
2015-06-09 11:26:12 -07:00
|
|
|
from tk_scene import TkSceneRoot
|
2017-01-25 16:40:59 -08:00
|
|
|
from mobject import Mobject, VMobject
|
2016-07-28 11:16:07 -07:00
|
|
|
from animation import Animation
|
2017-03-24 13:59:14 -07:00
|
|
|
from animation.animation import sync_animation_run_times_and_rate_funcs
|
2017-03-25 12:17:56 -07:00
|
|
|
from animation.transform import MoveToTarget
|
2015-03-22 16:15:29 -06:00
|
|
|
|
|
|
|
class Scene(object):
|
2016-02-27 16:32:53 -08:00
|
|
|
CONFIG = {
|
2017-05-09 13:14:08 -07:00
|
|
|
"camera_class" : Camera,
|
|
|
|
"camera_config" : {},
|
|
|
|
"frame_duration" : LOW_QUALITY_FRAME_DURATION,
|
|
|
|
"construct_args" : [],
|
|
|
|
"skip_animations" : False,
|
2017-05-16 14:29:14 -07:00
|
|
|
"write_to_movie" : False,
|
2017-05-09 13:14:08 -07:00
|
|
|
"save_frames" : False,
|
|
|
|
"output_directory" : MOVIE_DIR,
|
2017-05-09 14:54:50 -07:00
|
|
|
"name" : None,
|
2015-09-28 16:25:18 -07:00
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
2015-10-28 16:03:33 -07:00
|
|
|
digest_config(self, kwargs)
|
2017-02-02 15:36:24 -08:00
|
|
|
self.camera = self.camera_class(**self.camera_config)
|
2015-10-29 13:45:28 -07:00
|
|
|
self.mobjects = []
|
2017-05-09 12:25:28 -07:00
|
|
|
self.foreground_mobjects = []
|
2016-07-28 11:16:07 -07:00
|
|
|
self.num_plays = 0
|
2017-05-09 13:14:08 -07:00
|
|
|
self.saved_frames = []
|
2017-05-10 17:22:26 -07:00
|
|
|
self.shared_locals = {}
|
2017-05-09 14:54:50 -07:00
|
|
|
if self.name is None:
|
|
|
|
self.name = self.__class__.__name__
|
2015-10-29 13:45:28 -07:00
|
|
|
|
2016-08-10 10:26:07 -07:00
|
|
|
self.setup()
|
2017-05-09 13:14:08 -07:00
|
|
|
if self.write_to_movie:
|
|
|
|
self.open_movie_pipe()
|
2015-09-28 16:25:18 -07:00
|
|
|
self.construct(*self.construct_args)
|
2017-05-09 13:14:08 -07:00
|
|
|
if self.write_to_movie:
|
|
|
|
self.close_movie_pipe()
|
2015-06-10 22:00:35 -07:00
|
|
|
|
2016-08-10 10:26:07 -07:00
|
|
|
def setup(self):
|
2017-04-21 17:40:49 -07:00
|
|
|
"""
|
|
|
|
This is meant to be implement by any scenes which
|
|
|
|
are comonly subclassed, and have some common setup
|
|
|
|
involved before the construct method is called.
|
|
|
|
"""
|
|
|
|
pass
|
2016-08-10 10:26:07 -07:00
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
def construct(self):
|
|
|
|
pass #To be implemented in subclasses
|
2015-03-22 16:15:29 -06:00
|
|
|
|
|
|
|
def __str__(self):
|
2017-05-09 14:54:50 -07:00
|
|
|
return self.name
|
2015-10-12 19:39:46 -07:00
|
|
|
|
2015-04-03 16:41:25 -07:00
|
|
|
def set_name(self, name):
|
|
|
|
self.name = name
|
2015-06-09 11:26:12 -07:00
|
|
|
return self
|
2015-04-03 16:41:25 -07:00
|
|
|
|
2017-05-10 17:22:26 -07:00
|
|
|
def update_shared_locals(self, *keys):
|
|
|
|
"""
|
|
|
|
Often in constructing a scene, it's nice to refer to
|
|
|
|
what was a local variable from a previous subroutine,
|
|
|
|
so a dict of shared_locals is recorded, and it can be updated
|
|
|
|
by passing in the objects directly.
|
|
|
|
"""
|
|
|
|
caller_locals = inspect.currentframe().f_back.f_locals
|
|
|
|
self.shared_locals.update(dict([
|
|
|
|
(key, caller_locals[key])
|
|
|
|
for key in keys
|
|
|
|
]))
|
|
|
|
return self
|
|
|
|
|
2016-03-07 19:07:00 -08:00
|
|
|
### Only these methods should touch the camera
|
|
|
|
|
2016-02-27 16:29:11 -08:00
|
|
|
def set_camera(self, camera):
|
|
|
|
self.camera = camera
|
|
|
|
|
2015-10-29 13:45:28 -07:00
|
|
|
def get_frame(self):
|
2016-02-23 22:29:32 -08:00
|
|
|
return self.camera.get_image()
|
2015-10-29 13:45:28 -07:00
|
|
|
|
2016-11-24 10:38:52 -08:00
|
|
|
def set_camera_image(self, pixel_array):
|
|
|
|
self.camera.set_image(pixel_array)
|
|
|
|
|
2016-11-11 11:18:41 -08:00
|
|
|
def set_camera_background(self, background):
|
2016-11-23 17:50:25 -08:00
|
|
|
self.camera.set_background(background)
|
2016-11-11 11:18:41 -08:00
|
|
|
|
|
|
|
def reset_camera(self):
|
|
|
|
self.camera.reset()
|
|
|
|
|
|
|
|
def capture_mobjects_in_camera(self, mobjects, **kwargs):
|
|
|
|
self.camera.capture_mobjects(mobjects, **kwargs)
|
|
|
|
|
2016-03-08 23:13:41 -08:00
|
|
|
def update_frame(self, mobjects = None, background = None, **kwargs):
|
2016-09-07 13:38:05 -07:00
|
|
|
if "include_submobjects" not in kwargs:
|
|
|
|
kwargs["include_submobjects"] = False
|
2016-03-08 23:13:41 -08:00
|
|
|
if mobjects is None:
|
|
|
|
mobjects = self.mobjects
|
2016-03-07 19:07:00 -08:00
|
|
|
if background is not None:
|
2016-11-24 10:38:52 -08:00
|
|
|
self.set_camera_image(background)
|
2016-03-07 19:07:00 -08:00
|
|
|
else:
|
2016-11-11 11:18:41 -08:00
|
|
|
self.reset_camera()
|
|
|
|
self.capture_mobjects_in_camera(mobjects, **kwargs)
|
2016-03-17 23:53:59 -07:00
|
|
|
|
|
|
|
def freeze_background(self):
|
|
|
|
self.update_frame()
|
|
|
|
self.set_camera(Camera(self.get_frame()))
|
|
|
|
self.clear()
|
2016-03-07 19:07:00 -08:00
|
|
|
###
|
|
|
|
|
2016-09-06 16:48:04 -07:00
|
|
|
def extract_mobject_family_members(self, *mobjects):
|
2016-11-11 11:18:41 -08:00
|
|
|
return remove_list_redundancies(list(
|
2016-09-07 13:38:05 -07:00
|
|
|
it.chain(*[
|
|
|
|
m.submobject_family()
|
|
|
|
for m in mobjects
|
2017-01-25 16:40:59 -08:00
|
|
|
if not (isinstance(m, VMobject) and m.is_subpath)
|
2016-09-07 13:38:05 -07:00
|
|
|
])
|
2016-11-11 11:18:41 -08:00
|
|
|
))
|
2017-02-09 21:09:51 -08:00
|
|
|
|
|
|
|
def get_top_level_mobjects(self):
|
|
|
|
# Return only those which are not in the family
|
|
|
|
# of another mobject from the scene
|
|
|
|
mobjects = self.get_mobjects()
|
|
|
|
families = [m.submobject_family() for m in mobjects]
|
|
|
|
def is_top_level(mobject):
|
|
|
|
num_families = sum([
|
|
|
|
(mobject in family)
|
|
|
|
for family in families
|
|
|
|
])
|
|
|
|
return num_families == 1
|
|
|
|
return filter(is_top_level, mobjects)
|
2016-07-18 14:03:25 -07:00
|
|
|
|
2016-09-06 16:48:04 -07:00
|
|
|
def add(self, *mobjects_to_add):
|
2015-03-26 22:49:22 -06:00
|
|
|
"""
|
|
|
|
Mobjects will be displayed, from background to foreground,
|
|
|
|
in the order with which they are entered.
|
2016-07-18 14:03:25 -07:00
|
|
|
|
2016-09-07 13:38:05 -07:00
|
|
|
Scene class keeps track not just of the mobject directly added,
|
2016-09-06 16:48:04 -07:00
|
|
|
but also of every family member therein.
|
2015-03-26 22:49:22 -06:00
|
|
|
"""
|
2016-09-06 16:48:04 -07:00
|
|
|
if not all_elements_are_instances(mobjects_to_add, Mobject):
|
2015-10-29 13:45:28 -07:00
|
|
|
raise Exception("Adding something which is not a mobject")
|
2016-09-06 16:48:04 -07:00
|
|
|
mobjects_to_add = self.extract_mobject_family_members(*mobjects_to_add)
|
2016-09-07 13:38:05 -07:00
|
|
|
self.mobjects = list_update(self.mobjects, mobjects_to_add)
|
2015-06-09 11:26:12 -07:00
|
|
|
return self
|
2015-03-22 16:15:29 -06:00
|
|
|
|
2015-11-02 19:09:55 -08:00
|
|
|
def add_mobjects_among(self, values):
|
2015-10-09 19:53:38 -07:00
|
|
|
"""
|
|
|
|
So a scene can just add all mobjects it's defined up to that point
|
2015-11-02 19:09:55 -08:00
|
|
|
by calling add_mobjects_among(locals().values())
|
2015-10-09 19:53:38 -07:00
|
|
|
"""
|
2015-11-02 19:09:55 -08:00
|
|
|
mobjects = filter(lambda x : isinstance(x, Mobject), values)
|
|
|
|
self.add(*mobjects)
|
2015-11-02 13:03:01 -08:00
|
|
|
return self
|
2015-10-09 19:53:38 -07:00
|
|
|
|
2016-09-06 16:48:04 -07:00
|
|
|
def remove(self, *mobjects_to_remove):
|
|
|
|
if not all_elements_are_instances(mobjects_to_remove, Mobject):
|
2015-10-29 13:45:28 -07:00
|
|
|
raise Exception("Removing something which is not a mobject")
|
2016-09-06 16:48:04 -07:00
|
|
|
mobjects_to_remove = self.extract_mobject_family_members(*mobjects_to_remove)
|
2016-07-21 11:00:45 -07:00
|
|
|
self.mobjects = filter(
|
2016-09-06 16:48:04 -07:00
|
|
|
lambda m : m not in mobjects_to_remove,
|
2016-07-21 11:00:45 -07:00
|
|
|
self.mobjects
|
|
|
|
)
|
2017-04-06 15:25:19 -07:00
|
|
|
self.remove_mobjects_not_completely_on_screen()
|
2017-05-10 13:39:45 -07:00
|
|
|
self.remove_foreground_mobjects(*mobjects_to_remove)
|
2017-03-25 12:17:56 -07:00
|
|
|
return self
|
|
|
|
|
2017-04-06 15:25:19 -07:00
|
|
|
def remove_mobjects_not_completely_on_screen(self):
|
2017-03-25 12:17:56 -07:00
|
|
|
def should_keep(mobject):
|
2017-04-06 15:25:19 -07:00
|
|
|
return all([
|
2017-03-25 12:17:56 -07:00
|
|
|
submob in self.mobjects
|
2017-04-06 15:25:19 -07:00
|
|
|
for submob in mobject.family_members_with_points()
|
2017-03-25 12:17:56 -07:00
|
|
|
])
|
|
|
|
|
|
|
|
self.mobjects = filter(should_keep, self.mobjects)
|
2015-06-09 11:26:12 -07:00
|
|
|
return self
|
|
|
|
|
2017-05-09 12:25:28 -07:00
|
|
|
def add_foreground_mobjects(self, *mobjects):
|
|
|
|
self.foreground_mobjects = list_update(
|
|
|
|
self.foreground_mobjects,
|
|
|
|
mobjects
|
|
|
|
)
|
|
|
|
self.add(*mobjects)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def add_foreground_mobject(self, mobject):
|
|
|
|
return self.add_foreground_mobjects(mobject)
|
|
|
|
|
|
|
|
def remove_foreground_mobjects(self, *mobjects):
|
|
|
|
self.foreground_mobjects = filter(
|
|
|
|
lambda m : m not in mobjects,
|
|
|
|
self.foreground_mobjects
|
|
|
|
)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def remove_foreground_mobject(self, mobject):
|
|
|
|
return self.remove_foreground_mobjects(mobject)
|
|
|
|
|
2016-08-02 12:26:15 -07:00
|
|
|
def bring_to_front(self, *mobjects):
|
|
|
|
self.add(*mobjects)
|
2015-10-12 19:39:46 -07:00
|
|
|
return self
|
|
|
|
|
2016-08-02 12:26:15 -07:00
|
|
|
def bring_to_back(self, *mobjects):
|
|
|
|
self.remove(*mobjects)
|
|
|
|
self.mobjects = mobjects + self.mobjects
|
2015-10-12 19:39:46 -07:00
|
|
|
return self
|
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
def clear(self):
|
2015-10-29 13:45:28 -07:00
|
|
|
self.mobjects = []
|
2015-06-10 22:00:35 -07:00
|
|
|
return self
|
2015-03-30 17:51:26 -07:00
|
|
|
|
2016-07-18 11:50:26 -07:00
|
|
|
def get_mobjects(self):
|
|
|
|
return list(self.mobjects)
|
|
|
|
|
|
|
|
def get_mobject_copies(self):
|
|
|
|
return [m.copy() for m in self.mobjects]
|
|
|
|
|
2016-02-27 18:50:33 -08:00
|
|
|
def separate_moving_and_static_mobjects(self, *animations):
|
2016-07-19 11:07:26 -07:00
|
|
|
"""
|
|
|
|
"""
|
2016-09-06 16:48:04 -07:00
|
|
|
moving_mobjects = self.extract_mobject_family_members(
|
2017-05-09 12:25:28 -07:00
|
|
|
*[anim.mobject for anim in animations] + \
|
|
|
|
self.foreground_mobjects
|
2016-09-06 16:48:04 -07:00
|
|
|
)
|
2015-11-02 13:03:01 -08:00
|
|
|
static_mobjects = filter(
|
2016-09-06 16:48:04 -07:00
|
|
|
lambda m : m not in moving_mobjects,
|
|
|
|
self.mobjects
|
2015-11-02 13:03:01 -08:00
|
|
|
)
|
2016-02-27 12:44:52 -08:00
|
|
|
return moving_mobjects, static_mobjects
|
2015-03-22 16:15:29 -06:00
|
|
|
|
2016-02-27 12:44:52 -08:00
|
|
|
def get_time_progression(self, animations):
|
|
|
|
run_time = animations[0].run_time
|
2015-12-31 09:25:36 -08:00
|
|
|
times = np.arange(0, run_time, self.frame_duration)
|
|
|
|
time_progression = ProgressDisplay(times)
|
2016-02-27 18:50:33 -08:00
|
|
|
time_progression.set_description("".join([
|
2016-07-28 11:16:07 -07:00
|
|
|
"Animation %d: "%self.num_plays,
|
2016-02-27 18:50:33 -08:00
|
|
|
str(animations[0]),
|
|
|
|
(", etc." if len(animations) > 1 else ""),
|
|
|
|
]))
|
2016-02-27 12:44:52 -08:00
|
|
|
return time_progression
|
|
|
|
|
2016-07-28 11:16:07 -07:00
|
|
|
def compile_play_args_to_animation_list(self, *args):
|
|
|
|
"""
|
|
|
|
Eacn arg can either be an animation, or a mobject method
|
|
|
|
followed by that methods arguments.
|
|
|
|
|
|
|
|
This animation list is built by going through the args list,
|
|
|
|
and each animation is simply added, but when a mobject method
|
2016-09-06 14:17:39 -07:00
|
|
|
s hit, a MoveToTarget animation is built using the args that
|
2016-07-28 11:16:07 -07:00
|
|
|
follow up until either another animation is hit, another method
|
|
|
|
is hit, or the args list runs out.
|
|
|
|
"""
|
|
|
|
animations = []
|
2016-09-06 14:17:39 -07:00
|
|
|
state = {
|
|
|
|
"curr_method" : None,
|
|
|
|
"last_method" : None,
|
|
|
|
"method_args" : [],
|
2016-07-28 11:16:07 -07:00
|
|
|
}
|
2016-09-06 14:17:39 -07:00
|
|
|
def compile_method(state):
|
|
|
|
if state["curr_method"] is None:
|
2016-07-28 11:16:07 -07:00
|
|
|
return
|
2016-09-06 14:17:39 -07:00
|
|
|
mobject = state["curr_method"].im_self
|
|
|
|
if state["last_method"] and state["last_method"].im_self is mobject:
|
|
|
|
animations.pop()
|
|
|
|
#method should already have target then.
|
|
|
|
else:
|
|
|
|
mobject.target = mobject.copy()
|
|
|
|
state["curr_method"].im_func(
|
|
|
|
mobject.target, *state["method_args"]
|
|
|
|
)
|
|
|
|
animations.append(MoveToTarget(mobject))
|
|
|
|
state["last_method"] = state["curr_method"]
|
|
|
|
state["curr_method"] = None
|
|
|
|
state["method_args"] = []
|
2016-09-06 16:48:04 -07:00
|
|
|
|
2016-07-28 11:16:07 -07:00
|
|
|
for arg in args:
|
|
|
|
if isinstance(arg, Animation):
|
2016-09-06 14:17:39 -07:00
|
|
|
compile_method(state)
|
2016-07-28 11:16:07 -07:00
|
|
|
animations.append(arg)
|
|
|
|
elif inspect.ismethod(arg):
|
2016-09-06 14:17:39 -07:00
|
|
|
compile_method(state)
|
|
|
|
state["curr_method"] = arg
|
|
|
|
elif state["curr_method"] is not None:
|
|
|
|
state["method_args"].append(arg)
|
2016-09-07 22:04:24 -07:00
|
|
|
elif isinstance(arg, Mobject):
|
|
|
|
raise Exception("""
|
|
|
|
I think you may have invoked a method
|
|
|
|
you meant to pass in as a Scene.play argument
|
|
|
|
""")
|
2016-07-28 11:16:07 -07:00
|
|
|
else:
|
|
|
|
raise Exception("Invalid play arguments")
|
2016-09-06 14:17:39 -07:00
|
|
|
compile_method(state)
|
2016-07-28 11:16:07 -07:00
|
|
|
return animations
|
2016-02-27 18:50:33 -08:00
|
|
|
|
2016-07-28 11:16:07 -07:00
|
|
|
def play(self, *args, **kwargs):
|
|
|
|
if len(args) == 0:
|
2016-09-07 13:38:05 -07:00
|
|
|
warnings.warn("Called Scene.play with no animations")
|
2017-01-27 13:23:17 -08:00
|
|
|
return
|
2016-08-25 17:15:48 -07:00
|
|
|
if self.skip_animations:
|
|
|
|
kwargs["run_time"] = 0
|
|
|
|
|
2016-07-28 11:16:07 -07:00
|
|
|
animations = self.compile_play_args_to_animation_list(*args)
|
|
|
|
self.num_plays += 1
|
2016-07-19 11:07:26 -07:00
|
|
|
|
2017-03-24 13:59:14 -07:00
|
|
|
sync_animation_run_times_and_rate_funcs(*animations, **kwargs)
|
2016-02-27 12:44:52 -08:00
|
|
|
moving_mobjects, static_mobjects = \
|
2016-02-27 18:50:33 -08:00
|
|
|
self.separate_moving_and_static_mobjects(*animations)
|
2016-07-19 11:07:26 -07:00
|
|
|
self.update_frame(static_mobjects)
|
2016-03-07 19:07:00 -08:00
|
|
|
static_image = self.get_frame()
|
2016-02-27 12:44:52 -08:00
|
|
|
for t in self.get_time_progression(animations):
|
2015-04-03 16:41:25 -07:00
|
|
|
for animation in animations:
|
|
|
|
animation.update(t / animation.run_time)
|
2016-02-27 18:50:33 -08:00
|
|
|
self.update_frame(moving_mobjects, static_image)
|
2016-09-06 16:48:04 -07:00
|
|
|
self.add_frames(self.get_frame())
|
2016-09-07 13:38:05 -07:00
|
|
|
self.add(*moving_mobjects)
|
|
|
|
self.mobjects_from_last_animation = moving_mobjects
|
2016-07-19 11:07:26 -07:00
|
|
|
self.clean_up_animations(*animations)
|
|
|
|
return self
|
|
|
|
|
|
|
|
def clean_up_animations(self, *animations):
|
2015-04-03 16:41:25 -07:00
|
|
|
for animation in animations:
|
2017-03-25 12:17:56 -07:00
|
|
|
animation.clean_up(self)
|
2017-05-10 13:39:45 -07:00
|
|
|
self.add(*self.foreground_mobjects)
|
2015-06-09 11:26:12 -07:00
|
|
|
return self
|
2015-03-22 16:15:29 -06:00
|
|
|
|
2016-07-19 11:07:26 -07:00
|
|
|
def get_mobjects_from_last_animation(self):
|
|
|
|
if hasattr(self, "mobjects_from_last_animation"):
|
|
|
|
return self.mobjects_from_last_animation
|
|
|
|
return []
|
|
|
|
|
2015-03-26 22:49:22 -06:00
|
|
|
def dither(self, duration = DEFAULT_DITHER_TIME):
|
2016-08-25 17:15:48 -07:00
|
|
|
if self.skip_animations:
|
|
|
|
return self
|
2016-03-08 23:13:41 -08:00
|
|
|
self.update_frame()
|
2016-09-06 16:48:04 -07:00
|
|
|
self.add_frames(*[self.get_frame()]*int(duration / self.frame_duration))
|
2015-06-09 11:26:12 -07:00
|
|
|
return self
|
2015-03-22 16:15:29 -06:00
|
|
|
|
2017-03-20 14:37:51 -07:00
|
|
|
def force_skipping(self):
|
|
|
|
self.original_skipping_status = self.skip_animations
|
|
|
|
self.skip_animations = True
|
|
|
|
return self
|
|
|
|
|
|
|
|
def revert_to_original_skipping_status(self):
|
|
|
|
if hasattr(self, "original_skipping_status"):
|
|
|
|
self.skip_animations = self.original_skipping_status
|
|
|
|
return self
|
|
|
|
|
2016-09-06 16:48:04 -07:00
|
|
|
def add_frames(self, *frames):
|
2017-05-09 13:14:08 -07:00
|
|
|
if self.write_to_movie:
|
|
|
|
for frame in frames:
|
|
|
|
self.writing_process.stdin.write(frame.tostring())
|
|
|
|
if self.save_frames:
|
|
|
|
self.saved_frames += list(frames)
|
|
|
|
|
|
|
|
def repeat_frames(self, num = 1):
|
|
|
|
assert(self.save_frames)
|
|
|
|
self.add_frames(*self.saved_frames*num)
|
2016-01-30 14:44:45 -08:00
|
|
|
return self
|
|
|
|
|
|
|
|
def invert_colors(self):
|
|
|
|
white_frame = 255*np.ones(self.get_frame().shape, dtype = 'uint8')
|
2017-05-09 13:14:08 -07:00
|
|
|
self.saved_frames = [
|
2016-01-30 14:44:45 -08:00
|
|
|
white_frame-frame
|
2017-05-09 13:14:08 -07:00
|
|
|
for frame in self.saved_frames
|
2016-01-30 14:44:45 -08:00
|
|
|
]
|
|
|
|
return self
|
|
|
|
|
2015-06-09 11:26:12 -07:00
|
|
|
def show_frame(self):
|
2016-03-08 23:13:41 -08:00
|
|
|
self.update_frame()
|
2015-04-03 16:41:25 -07:00
|
|
|
Image.fromarray(self.get_frame()).show()
|
|
|
|
|
2015-06-09 11:26:12 -07:00
|
|
|
def preview(self):
|
|
|
|
TkSceneRoot(self)
|
|
|
|
|
2017-05-09 13:14:08 -07:00
|
|
|
def save_image(self, name = None):
|
|
|
|
path = os.path.join(self.output_directory, "images")
|
2015-12-23 14:51:19 -08:00
|
|
|
file_name = (name or str(self)) + ".png"
|
|
|
|
full_path = os.path.join(path, file_name)
|
|
|
|
if not os.path.exists(path):
|
|
|
|
os.makedirs(path)
|
2016-12-26 07:10:38 -08:00
|
|
|
self.update_frame()
|
2015-12-23 14:51:19 -08:00
|
|
|
Image.fromarray(self.get_frame()).save(full_path)
|
2015-05-17 15:08:51 -07:00
|
|
|
|
2016-02-23 22:29:32 -08:00
|
|
|
def get_movie_file_path(self, name, extension):
|
2017-05-09 13:14:08 -07:00
|
|
|
file_path = os.path.join(self.output_directory, name)
|
2016-02-23 22:29:32 -08:00
|
|
|
if not file_path.endswith(extension):
|
|
|
|
file_path += extension
|
2017-05-09 13:14:08 -07:00
|
|
|
if not os.path.exists(self.output_directory):
|
|
|
|
os.makedirs(self.output_directory)
|
2016-02-23 22:29:32 -08:00
|
|
|
return file_path
|
|
|
|
|
2017-05-09 13:14:08 -07:00
|
|
|
def open_movie_pipe(self):
|
|
|
|
name = str(self)
|
2016-02-23 22:29:32 -08:00
|
|
|
file_path = self.get_movie_file_path(name, ".mp4")
|
2017-06-06 13:38:21 -07:00
|
|
|
temp_file_path = file_path.replace(".mp4", "Temp.mp4")
|
|
|
|
print "Writing to %s"%temp_file_path
|
|
|
|
self.args_to_rename_file = (temp_file_path, file_path)
|
2016-02-23 22:29:32 -08:00
|
|
|
|
|
|
|
fps = int(1/self.frame_duration)
|
2016-02-27 18:50:33 -08:00
|
|
|
height, width = self.camera.pixel_shape
|
2016-02-23 22:29:32 -08:00
|
|
|
|
|
|
|
command = [
|
|
|
|
FFMPEG_BIN,
|
|
|
|
'-y', # overwrite output file if it exists
|
|
|
|
'-f', 'rawvideo',
|
|
|
|
'-vcodec','rawvideo',
|
2016-02-27 18:50:33 -08:00
|
|
|
'-s', '%dx%d'%(width, height), # size of one frame
|
2016-02-23 22:29:32 -08:00
|
|
|
'-pix_fmt', 'rgb24',
|
2016-09-06 16:48:04 -07:00
|
|
|
'-r', str(fps), # frames per second
|
|
|
|
'-i', '-', # The imput comes from a pipe
|
|
|
|
'-an', # Tells FFMPEG not to expect any audio
|
2016-02-23 22:29:32 -08:00
|
|
|
'-vcodec', 'mpeg',
|
|
|
|
'-c:v', 'libx264',
|
|
|
|
'-pix_fmt', 'yuv420p',
|
|
|
|
'-loglevel', 'error',
|
2017-06-06 13:38:21 -07:00
|
|
|
temp_file_path,
|
2016-02-23 22:29:32 -08:00
|
|
|
]
|
2017-05-09 13:14:08 -07:00
|
|
|
self.writing_process = sp.Popen(command, stdin=sp.PIPE)
|
|
|
|
|
|
|
|
def close_movie_pipe(self):
|
|
|
|
self.writing_process.stdin.close()
|
|
|
|
self.writing_process.wait()
|
2017-06-06 13:38:21 -07:00
|
|
|
os.rename(*self.args_to_rename_file)
|
|
|
|
|
2016-02-23 22:29:32 -08:00
|
|
|
|
2015-08-17 13:19:34 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-03-22 16:15:29 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|