diff --git a/__init__.py b/__init__.py index e69de29b..7718a95c 100644 --- a/__init__.py +++ b/__init__.py @@ -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 * \ No newline at end of file diff --git a/displayer.py b/displayer.py index d3f3b1c4..b8cc78b5 100644 --- a/displayer.py +++ b/displayer.py @@ -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" diff --git a/sample_script.py b/sample_script.py index db6736e7..4d4b7134 100644 --- a/sample_script.py +++ b/sample_script.py @@ -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() \ No newline at end of file diff --git a/scene/counting_scene.py b/scene/counting_scene.py new file mode 100644 index 00000000..80c18276 --- /dev/null +++ b/scene/counting_scene.py @@ -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 \ No newline at end of file diff --git a/scene/scene.py b/scene/scene.py index 86ce6558..6bd501e9 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -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] + + + + + + + + + + + + + + + + + + + +