Implemented SceneFromVideo

This commit is contained in:
Grant Sanderson 2015-08-17 13:19:34 -07:00
parent 7087899f12
commit 461da7049b
5 changed files with 168 additions and 92 deletions

View file

@ -0,0 +1,10 @@
from animation import *
from mobject import *
from scene import *
from scripts import *
from constants import *
from helpers import *
from displayer import *
from region import *
from script_wrapper import *
from tex_utils import *

View file

@ -94,7 +94,7 @@ def write_to_gif(scene, name):
temppath = os.path.join(GIF_DIR, "Temp.gif")
print "Writing " + name + "..."
images = [Image.fromarray(frame) for frame in scene.frames]
writeGif(temppath, images, scene.display_config["frame_duration"])
writeGif(temppath, images, scene.frame_duration)
print "Compressing..."
os.system("gifsicle -O " + temppath + " > " + filepath)
os.system("rm " + temppath)
@ -109,7 +109,7 @@ def write_to_movie(scene, name):
filedir = "/".join(filepath.split("/")[:-1])
if not os.path.exists(filedir):
os.makedirs(filedir)
rate = int(1/scene.display_config["frame_duration"])
rate = int(1/scene.frame_duration)
tmp_stem = os.path.join(TMP_IMAGE_DIR, name.replace("/", "_"))
suffix = "-%04d.png"

View file

@ -10,14 +10,20 @@ from animation import *
from mobject import *
from constants import *
from region import *
from scene import Scene
from scene import Scene, SceneFromVideo
from script_wrapper import command_line_create_scene
class SampleScene(Scene):
class SampleScene(SceneFromVideo):
def construct(self):
three = tex_mobject("3")
self.add(three)
path = os.path.join(MOVIE_DIR, "LogoGeneration.mp4")
SceneFromVideo.construct(self, path)
self.animate_over_time_range(
0, 3,
ApplyMethod(Dot().to_edge(LEFT).to_edge, RIGHT)
)
if __name__ == "__main__":
command_line_create_scene()

83
scene/counting_scene.py Normal file
View file

@ -0,0 +1,83 @@
from scene import Scene
from mobject import *
from animation import *
from region import *
from constants import *
from helpers import *
class CountingScene(Scene):
def count(self, items, item_type = "mobject", *args, **kwargs):
if item_type == "mobject":
self.count_mobjects(items, *args, **kwargs)
elif item_type == "region":
self.count_regions(items, *args, **kwargs)
else:
raise Exception("Unknown item_type, should be mobject or region")
return self
def count_mobjects(
self, mobjects, mode = "highlight",
color = "red",
display_numbers = True,
num_offset = DEFAULT_COUNT_NUM_OFFSET,
run_time = DEFAULT_COUNT_RUN_TIME):
"""
Note, leaves final number mobject as "number" attribute
mode can be "highlight", "show_creation" or "show", otherwise
a warning is given and nothing is animating during the count
"""
if len(mobjects) > 50: #TODO
raise Exception("I don't know if you should be counting \
too many mobjects...")
if len(mobjects) == 0:
raise Exception("Counting mobject list of length 0")
if mode not in ["highlight", "show_creation", "show"]:
raise Warning("Unknown mode")
frame_time = run_time / len(mobjects)
if mode == "highlight":
self.add(*mobjects)
for mob, num in zip(mobjects, it.count(1)):
if display_numbers:
num_mob = tex_mobject(str(num))
num_mob.center().shift(num_offset)
self.add(num_mob)
if mode == "highlight":
original_color = mob.color
mob.highlight(color)
self.dither(frame_time)
mob.highlight(original_color)
if mode == "show_creation":
self.animate(ShowCreation(mob, run_time = frame_time))
if mode == "show":
self.add(mob)
self.dither(frame_time)
if display_numbers:
self.remove(num_mob)
if display_numbers:
self.add(num_mob)
self.number = num_mob
return self
def count_regions(self, regions,
mode = "one_at_a_time",
num_offset = DEFAULT_COUNT_NUM_OFFSET,
run_time = DEFAULT_COUNT_RUN_TIME,
**unused_kwargsn):
if mode not in ["one_at_a_time", "show_all"]:
raise Warning("Unknown mode")
frame_time = run_time / (len(regions))
for region, count in zip(regions, it.count(1)):
num_mob = tex_mobject(str(count))
num_mob.center().shift(num_offset)
self.add(num_mob)
self.highlight_region(region)
self.dither(frame_time)
if mode == "one_at_a_time":
self.reset_background()
self.remove(num_mob)
self.add(num_mob)
self.number = num_mob
return self

View file

@ -8,6 +8,7 @@ import os
import copy
import progressbar
import inspect
import cv2
from helpers import *
from mobject import *
@ -33,7 +34,7 @@ class Scene(object):
#TODO, Error checking?
else:
self.original_background = np.zeros(
(display_config["height"], display_config["width"], 3),
(self.display_config["height"], self.display_config["width"], 3),
dtype = 'uint8'
)
self.background = self.original_background
@ -105,10 +106,11 @@ class Scene(object):
for t in np.arange(0, run_time, self.frame_duration):
progress_bar.update(t)
new_frame = background
for animation in animations:
animation.update(t / animation.run_time)
new_frame = disp.paint_mobject(animation.mobject, new_frame)
new_frame = disp.paint_mobject(
CompoundMobject(*moving_mobjects), background
)
self.frames.append(new_frame)
for animation in animations:
animation.clean_up()
@ -116,7 +118,7 @@ class Scene(object):
progress_bar.finish()
return self
def animate_over_time_range(self, t0, t1, animation):
def animate_over_time_range(self, t0, t1, *animations):
needed_scene_time = max(abs(t0), abs(t1))
existing_scene_time = len(self.frames)*self.frame_duration
if existing_scene_time < needed_scene_time:
@ -129,95 +131,22 @@ class Scene(object):
t1 = float(t1)%existing_scene_time
t0, t1 = min(t0, t1), max(t0, t1)
moving_mobjects = [anim.mobject for anim in animations]
for t in np.arange(t0, t1, self.frame_duration):
animation.update((t-t0)/(t1 - t0))
for animation in animations:
animation.update((t-t0)/(t1 - t0))
index = int(t/self.frame_duration)
self.frames[index] = disp.paint_mobject(
animation.mobject, self.frames[index]
CompoundMobject(*moving_mobjects), self.frames[index]
)
animation.clean_up()
return self
def count(self, items, item_type = "mobject", *args, **kwargs):
if item_type == "mobject":
self.count_mobjects(items, *args, **kwargs)
elif item_type == "region":
self.count_regions(items, *args, **kwargs)
else:
raise Exception("Unknown item_type, should be mobject or region")
return self
def count_mobjects(
self, mobjects, mode = "highlight",
color = "red",
display_numbers = True,
num_offset = DEFAULT_COUNT_NUM_OFFSET,
run_time = DEFAULT_COUNT_RUN_TIME):
"""
Note, leaves final number mobject as "number" attribute
mode can be "highlight", "show_creation" or "show", otherwise
a warning is given and nothing is animating during the count
"""
if len(mobjects) > 50: #TODO
raise Exception("I don't know if you should be counting \
too many mobjects...")
if len(mobjects) == 0:
raise Exception("Counting mobject list of length 0")
if mode not in ["highlight", "show_creation", "show"]:
raise Warning("Unknown mode")
frame_time = run_time / len(mobjects)
if mode == "highlight":
self.add(*mobjects)
for mob, num in zip(mobjects, it.count(1)):
if display_numbers:
num_mob = tex_mobject(str(num))
num_mob.center().shift(num_offset)
self.add(num_mob)
if mode == "highlight":
original_color = mob.color
mob.highlight(color)
self.dither(frame_time)
mob.highlight(original_color)
if mode == "show_creation":
self.animate(ShowCreation(mob, run_time = frame_time))
if mode == "show":
self.add(mob)
self.dither(frame_time)
if display_numbers:
self.remove(num_mob)
if display_numbers:
self.add(num_mob)
self.number = num_mob
return self
def count_regions(self, regions,
mode = "one_at_a_time",
num_offset = DEFAULT_COUNT_NUM_OFFSET,
run_time = DEFAULT_COUNT_RUN_TIME,
**unused_kwargsn):
if mode not in ["one_at_a_time", "show_all"]:
raise Warning("Unknown mode")
frame_time = run_time / (len(regions))
for region, count in zip(regions, it.count(1)):
num_mob = tex_mobject(str(count))
num_mob.center().shift(num_offset)
self.add(num_mob)
self.highlight_region(region)
self.dither(frame_time)
if mode == "one_at_a_time":
self.reset_background()
self.remove(num_mob)
self.add(num_mob)
self.number = num_mob
for animation in animations:
animation.clean_up()
return self
def get_frame(self):
frame = self.background
for mob in self.mobjects:
frame = disp.paint_mobject(mob, frame)
return frame
return disp.paint_mobject(
CompoundMobject(*self.mobjects), self.background
)
def dither(self, duration = DEFAULT_DITHER_TIME):
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
@ -254,6 +183,54 @@ class Scene(object):
@staticmethod
def args_to_string(*args):
return ""
class SceneFromVideo(Scene):
def construct(self, file_name, freeze_last_frame = True):
cap = cv2.VideoCapture(file_name)
self.shape = (
int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT)),
int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH))
)
self.frame_duration = 1.0/cap.get(cv2.cv.CV_CAP_PROP_FPS)
frame_count = int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))
print "Reading in " + file_name + "..."
progress_bar = progressbar.ProgressBar(maxval=frame_count)
progress_bar.start()
while(cap.isOpened()):
returned, frame = cap.read()
if not returned:
break
b, g, r = cv2.split(frame)
self.frames.append(cv2.merge([r, g, b]))
progress_bar.update(len(self.frames))
cap.release()
progress_bar.finish()
if freeze_last_frame and len(self.frames) > 0:
self.original_background = self.background = self.frames[-1]