mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Scenes now pass frames directly to ffmpeg pipe when writing movies, rather than saving them all up in bulk then passing in the list
This commit is contained in:
parent
274b180eaa
commit
18b2267c7e
2 changed files with 68 additions and 81 deletions
|
@ -51,7 +51,8 @@ def get_configuration(sys_argv):
|
|||
"camera_config" : PRODUCTION_QUALITY_CAMERA_CONFIG,
|
||||
"frame_duration" : PRODUCTION_QUALITY_FRAME_DURATION,
|
||||
"preview" : False,
|
||||
"write" : False,
|
||||
"write_to_movie" : False,
|
||||
"save_all_frames" : False,
|
||||
"save_image" : False,
|
||||
"quiet" : False,
|
||||
"write_all" : False,
|
||||
|
@ -66,11 +67,12 @@ def get_configuration(sys_argv):
|
|||
config["frame_duration"] = LOW_QUALITY_FRAME_DURATION
|
||||
if opt == '-p':
|
||||
config["preview"] = True
|
||||
config["save_all_frames"] = True
|
||||
if opt == '-m':
|
||||
config["camera_config"] = MEDIUM_QUALITY_CAMERA_CONFIG
|
||||
config["frame_duration"] = MEDIUM_QUALITY_FRAME_DURATION
|
||||
if opt == '-w':
|
||||
config["write"] = True
|
||||
config["write_to_movie"] = True
|
||||
if opt == '-s':
|
||||
config["save_image"] = True
|
||||
if opt in ['-q', '-a']:
|
||||
|
@ -80,10 +82,10 @@ def get_configuration(sys_argv):
|
|||
if opt == '-o':
|
||||
config["output_name"] = arg
|
||||
#By default, write to file
|
||||
actions = ["write", "preview", "save_image"]
|
||||
actions = ["write_to_movie", "preview", "save_image"]
|
||||
if not any([config[key] for key in actions]):
|
||||
config["write"] = True
|
||||
config["skip_animations"] = config["save_image"] and not config["write"]
|
||||
config["write_to_movie"] = True
|
||||
config["skip_animations"] = config["save_image"] and not config["write_to_movie"]
|
||||
|
||||
if len(args) == 0:
|
||||
print HELP_MESSAGE
|
||||
|
@ -104,10 +106,7 @@ def handle_scene(scene, **config):
|
|||
if config["save_image"]:
|
||||
if not config["write_all"]:
|
||||
scene.show_frame()
|
||||
path = os.path.join(MOVIE_DIR, config["movie_prefix"])
|
||||
scene.save_image(path, output_name)
|
||||
if config["write"]:
|
||||
scene.write_to_movie(os.path.join(config["movie_prefix"], output_name))
|
||||
scene.save_image(output_name)
|
||||
|
||||
if config["quiet"]:
|
||||
sys.stdout.close()
|
||||
|
@ -167,12 +166,22 @@ def main():
|
|||
scene_names_to_classes = dict(
|
||||
inspect.getmembers(module, is_scene)
|
||||
)
|
||||
config["movie_prefix"] = config["file"].replace(".py", "")
|
||||
scene_kwargs = {
|
||||
"camera_config" : config["camera_config"],
|
||||
"frame_duration" : config["frame_duration"],
|
||||
"skip_animations" : config["skip_animations"],
|
||||
}
|
||||
config["output_directory"] = os.path.join(
|
||||
MOVIE_DIR,
|
||||
config["file"].replace(".py", "")
|
||||
)
|
||||
|
||||
scene_kwargs = dict([
|
||||
(key, config[key])
|
||||
for key in [
|
||||
"camera_config",
|
||||
"frame_duration",
|
||||
"skip_animations",
|
||||
"write_to_movie",
|
||||
"save_all_frames",
|
||||
"output_directory",
|
||||
]
|
||||
])
|
||||
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
||||
try:
|
||||
handle_scene(SceneClass(**scene_kwargs), **config)
|
||||
|
|
|
@ -26,17 +26,24 @@ class Scene(object):
|
|||
"frame_duration" : LOW_QUALITY_FRAME_DURATION,
|
||||
"construct_args" : [],
|
||||
"skip_animations" : False,
|
||||
"write_to_movie" : True,
|
||||
"save_frames" : False,
|
||||
"output_directory" : MOVIE_DIR,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
self.camera = self.camera_class(**self.camera_config)
|
||||
self.frames = []
|
||||
self.mobjects = []
|
||||
self.foreground_mobjects = []
|
||||
self.num_plays = 0
|
||||
self.saved_frames = []
|
||||
|
||||
self.setup()
|
||||
if self.write_to_movie:
|
||||
self.open_movie_pipe()
|
||||
self.construct(*self.construct_args)
|
||||
if self.write_to_movie:
|
||||
self.close_movie_pipe()
|
||||
|
||||
def setup(self):
|
||||
"""
|
||||
|
@ -313,31 +320,6 @@ class Scene(object):
|
|||
return self.mobjects_from_last_animation
|
||||
return []
|
||||
|
||||
def play_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:
|
||||
self.dither(needed_scene_time - existing_scene_time)
|
||||
existing_scene_time = needed_scene_time
|
||||
#So negative values may be used
|
||||
if t0 < 0:
|
||||
t0 = float(t0)%existing_scene_time
|
||||
if t1 < 0:
|
||||
t1 = float(t1)%existing_scene_time
|
||||
t0, t1 = min(t0, t1), max(t0, t1)
|
||||
|
||||
moving_mobjects, static_mobjects = \
|
||||
self.separate_moving_and_static_mobjects(*animations)
|
||||
for t in np.arange(t0, t1, self.frame_duration):
|
||||
for animation in animations:
|
||||
animation.update((t-t0)/(t1 - t0))
|
||||
index = int(t/self.frame_duration)
|
||||
self.update_frame(moving_mobjects, self.frames[index])
|
||||
self.frames[index] = self.get_frame()
|
||||
for animation in animations:
|
||||
animation.clean_up()
|
||||
return self
|
||||
|
||||
def dither(self, duration = DEFAULT_DITHER_TIME):
|
||||
if self.skip_animations:
|
||||
return self
|
||||
|
@ -356,21 +338,22 @@ class Scene(object):
|
|||
return self
|
||||
|
||||
def add_frames(self, *frames):
|
||||
self.frames += list(frames)
|
||||
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):
|
||||
self.frames = self.frames*num
|
||||
return self
|
||||
|
||||
def reverse_frames(self):
|
||||
self.frames.reverse()
|
||||
def repeat_frames(self, num = 1):
|
||||
assert(self.save_frames)
|
||||
self.add_frames(*self.saved_frames*num)
|
||||
return self
|
||||
|
||||
def invert_colors(self):
|
||||
white_frame = 255*np.ones(self.get_frame().shape, dtype = 'uint8')
|
||||
self.frames = [
|
||||
self.saved_frames = [
|
||||
white_frame-frame
|
||||
for frame in self.frames
|
||||
for frame in self.saved_frames
|
||||
]
|
||||
return self
|
||||
|
||||
|
@ -381,8 +364,8 @@ class Scene(object):
|
|||
def preview(self):
|
||||
TkSceneRoot(self)
|
||||
|
||||
def save_image(self, directory = MOVIE_DIR, name = None):
|
||||
path = os.path.join(directory, "images")
|
||||
def save_image(self, name = None):
|
||||
path = os.path.join(self.output_directory, "images")
|
||||
file_name = (name or str(self)) + ".png"
|
||||
full_path = os.path.join(path, file_name)
|
||||
if not os.path.exists(path):
|
||||
|
@ -391,19 +374,14 @@ class Scene(object):
|
|||
Image.fromarray(self.get_frame()).save(full_path)
|
||||
|
||||
def get_movie_file_path(self, name, extension):
|
||||
file_path = os.path.join(MOVIE_DIR, name)
|
||||
file_path = os.path.join(self.output_directory, name)
|
||||
if not file_path.endswith(extension):
|
||||
file_path += extension
|
||||
directory = os.path.split(file_path)[0]
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
if not os.path.exists(self.output_directory):
|
||||
os.makedirs(self.output_directory)
|
||||
return file_path
|
||||
|
||||
def write_to_movie(self, name = None):
|
||||
if len(self.frames) == 0:
|
||||
print "No frames, so I'm not writing anything"
|
||||
return
|
||||
if name is None:
|
||||
def open_movie_pipe(self):
|
||||
name = str(self)
|
||||
file_path = self.get_movie_file_path(name, ".mp4")
|
||||
print "Writing to %s"%file_path
|
||||
|
@ -427,11 +405,11 @@ class Scene(object):
|
|||
'-loglevel', 'error',
|
||||
file_path,
|
||||
]
|
||||
process = sp.Popen(command, stdin=sp.PIPE)
|
||||
for frame in self.frames:
|
||||
process.stdin.write(frame.tostring())
|
||||
process.stdin.close()
|
||||
process.wait()
|
||||
self.writing_process = sp.Popen(command, stdin=sp.PIPE)
|
||||
|
||||
def close_movie_pipe(self):
|
||||
self.writing_process.stdin.close()
|
||||
self.writing_process.wait()
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue