def test_CompositeAudioClip_by__init__(): """The difference between the CompositeAudioClip returned by ``concatenate_audioclips`` and a CompositeAudioClip created using the class directly, is that audios in ``concatenate_audioclips`` are played one after other and AudioClips passed to CompositeAudioClip can be played at different times, it depends on their ``start`` attributes. """ frequencies = [440, 880, 1760] durations = [2, 5, 1] fpss = [44100, 22050, 11025] starts = [0, 1, 2] clips = [ AudioClip( lambda t: [np.sin(frequency * 2 * np.pi * t)], duration=duration, fps=fps ).with_start(start) for frequency, duration, fps, start in zip(frequencies, durations, fpss, starts) ] compound_clip = CompositeAudioClip(clips) # should return a CompositeAudioClip assert isinstance(compound_clip, CompositeAudioClip) # fps of the greatest fps passed into it assert compound_clip.fps == 44100 # duration depends on clips starts and durations ends = [start + duration for start, duration in zip(starts, durations)] assert compound_clip.duration == max(ends) assert list(compound_clip.ends) == ends assert list(compound_clip.starts) == starts # channels are maximum number of channels of the clips assert compound_clip.nchannels == max(clip.nchannels for clip in clips)
def add_voice(video_path, term): videoclip = VideoFileClip(video_path) audioclip = AudioFileClip(speech(term)) new_audioclip = CompositeAudioClip([videoclip.audio, audioclip]) videoclip.audio = new_audioclip videoclip.write_videofile(video_path) os.remove('/home/benjamim/PycharmProjects/Nyte/ImagesAndSounds/' + term[0:3] + '.mp3')
def calculate_loudness(audio_subclip, fps): CompositeAudioClip([audio_subclip]).write_audiofile(AUDIO_SUBCLIP_NAME, fps=fps) data, rate = sf.read(AUDIO_SUBCLIP_NAME) # load audio meter = pyln.Meter(rate) # create BS.1770 meter loudness = meter.integrated_loudness(data) # measure loudness return loudness
def __init__(self, clips, size=None, bg_color=None, transparent=False, ismask=False): if size is None: size = clips[0].size if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.transparent = transparent self.bg_color = bg_color self.bg = ColorClip(size, col=self.bg_color).get_frame(0) # compute duration ends = [c.end for c in self.clips] if not any([(e is None) for e in ends]): self.duration = max(ends) self.end = max(ends) # compute audio audioclips = [v.audio for v in self.clips if v.audio != None] if len(audioclips) > 0: self.audio = CompositeAudioClip(audioclips) # compute mask if transparent: maskclips = [ c.mask.set_pos(c.pos) for c in self.clips if c.mask is not None ] if maskclips != []: self.mask = CompositeVideoClip(maskclips, self.size, transparent=False, ismask=True) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = self.bg for c in self.playing_clips(t): f = c.blit_on(f, t) return f self.make_frame = make_frame
def audio_compose(audio_or_file1, audio_or_file2, t1=0, t2=None): """ Concatenates or superposes two sounds. Ajoute ou superpose deux sons. @param audio_or_file1 son 1 @param audio_or_file2 son 2 @param t1 start of the first sound @param t2 start of the second sound (or None to add it ad @return new sound Example: :: from code_beatrix.art.video import audio_compose son = audio_compose('son1.mp3', 'son2.mp3', 0, 10) """ with AudioContext(audio_or_file1) as audio1: with AudioContext(audio_or_file2) as audio2: add = [] if t1 != 0: add.append(audio1.set_start(t1)) else: add.append(audio1) if t2 is None: add.append(audio2.set_start(audio1.duration + t1)) else: add.append(audio2.set_start(t2)) comp = CompositeAudioClip(add) fps1 = audio1.fps if hasattr(audio1, 'fps') else None fps2 = audio2.fps if hasattr(audio2, 'fps') else None if fps1 is not None and fps2 is not None: fps = max(fps1, fps2) return comp.set_fps(fps) elif fps1 is None and fps2 is None: return comp else: return comp.set_fps(fps1 or fps2)
def add_sfx(self, clip): logger.info('Adding sfx...') new_audio = [clip.audio] for i in range(self.sfx_num): sfx_path = config.SFX_PATH + str(i) + '.mp3' sfx_clip = AudioFileClip(sfx_path) if sfx_clip.duration > clip.duration: sfx_clip = sfx_clip.set_duration(clip.duration) new_audio.append(sfx_clip) else: new_audio.append(sfx_clip) return clip.set_audio(CompositeAudioClip(new_audio))
def images_to_video_with_audio(images: List[np.ndarray], output_dir: str, video_name: str, audios: List[str], sr: int, fps: int = 1, quality: Optional[float] = 5, **kwargs): r"""Calls imageio to run FFMPEG on a list of images. For more info on parameters, see https://imageio.readthedocs.io/en/stable/format_ffmpeg.html Args: images: The list of images. Images should be HxWx3 in RGB order. output_dir: The folder to put the video in. video_name: The name for the video. audios: raw audio files fps: Frames per second for the video. Not all values work with FFMPEG, use at your own risk. quality: Default is 5. Uses variable bit rate. Highest quality is 10, lowest is 0. Set to None to prevent variable bitrate flags to FFMPEG so you can manually specify them using output_params instead. Specifying a fixed bitrate using ‘bitrate’ disables this parameter. """ assert 0 <= quality <= 10 if not os.path.exists(output_dir): os.makedirs(output_dir) video_name = video_name.replace(" ", "_").replace("\n", "_") + ".mp4" assert len(images) == len(audios) * fps audio_clips = [] temp_file_name = '/tmp/{}.wav'.format(random.randint(0, 10000)) # use amplitude scaling factor to reduce the volume of sounds amplitude_scaling_factor = 100 for i, audio in enumerate(audios): # def f(t): # return audio[0, t], audio[1: t] # # audio_clip = mpy.AudioClip(f, duration=1, fps=audio.shape[1]) wavfile.write(temp_file_name, sr, audio.T / amplitude_scaling_factor) audio_clip = mpy.AudioFileClip(temp_file_name) audio_clip = audio_clip.set_duration(1) audio_clip = audio_clip.set_start(i) audio_clips.append(audio_clip) composite_audio_clip = CompositeAudioClip(audio_clips) video_clip = mpy.ImageSequenceClip(images, fps=fps) video_with_new_audio = video_clip.set_audio(composite_audio_clip) video_with_new_audio.write_videofile(os.path.join(output_dir, video_name)) os.remove(temp_file_name)
def audio_delay(clip, offset=0.2, n_repeats=8, decay=1): """Repeats audio certain number of times at constant intervals multiplying their volume levels using a linear space in the range 1 to ``decay`` argument value. Parameters ---------- offset : float, optional Gap between repetitions start times, in seconds. n_repeats : int, optional Number of repetitions (without including the clip itself). decay : float, optional Multiplication factor for the volume level of the last repetition. Each repetition will have a value in the linear function between 1 and this value, increasing or decreasing constantly. Keep in mind that the last repetition will be muted if this is 0, and if is greater than 1, the volume will increase for each repetition. Examples -------- >>> from moviepy import * >>> videoclip = AudioFileClip('myaudio.wav').fx( ... audio_delay, offset=.2, n_repeats=10, decayment=.2 ... ) >>> # stereo A note >>> make_frame = lambda t: np.array( ... [np.sin(440 * 2 * np.pi * t), np.sin(880 * 2 * np.pi * t)] ... ).T ... clip = AudioClip(make_frame=make_frame, duration=0.1, fps=44100) ... clip = audio_delay(clip, offset=.2, n_repeats=11, decay=0) """ decayments = np.linspace(1, max(0, decay), n_repeats + 1) return CompositeAudioClip([ clip.copy(), *[ multiply_volume(clip.with_start( (rep + 1) * offset), decayments[rep + 1]) for rep in range(n_repeats) ], ])
def test_find_audio_period(mono_wave, stereo_wave, wave_type): if wave_type == "mono": wave1 = mono_wave(freq=400) wave2 = mono_wave(freq=100) else: wave1 = stereo_wave(left_freq=400, right_freq=220) wave2 = stereo_wave(left_freq=100, right_freq=200) clip = CompositeAudioClip([ AudioClip(make_frame=wave1, duration=0.3, fps=22050), multiply_volume( AudioClip(make_frame=wave2, duration=0.3, fps=22050), 0, end_time=0.1, ), ]) loop_clip = loop(clip, 4) assert round(find_audio_period(loop_clip), 6) == pytest.approx(0.29932, 0.1)
def make_round(stack: ExitStack, output_config: OutputConfig, r_i: int, cutter_lock: Lock, max_threads_semaphore: Semaphore): max_threads_semaphore.acquire() round_config = output_config.rounds[r_i] ext, codec = _get_ext_codec(output_config.raw) # Skip rounds that have been saved if round_config._is_on_disk: name = get_round_name(output_config.name, round_config.name, ext) max_threads_semaphore.release() return name # Assemble beatmeter video from beat images bmcfg = (round_config.beatmeter_config if round_config.bmcfg else None) if round_config.beatmeter is not None: print("\r\nAssembling beatmeter #{}...".format(r_i + 1)) beatmeter_thread = ThreadWithReturnValue( target=lambda: make_beatmeter( stack, round_config.beatmeter, bmcfg.fps if bmcfg else output_config.fps, round_config.duration, (output_config.xdim, output_config.ydim), ), daemon=True, ) beatmeter_thread.start() # Get list of clips cut from sources using chosen cutter # TODO: get duration, bpm from music track; generate beatmeter print("\r\nLoading sources for round #%i..." % (r_i + 1)) cutter = get_cutter(stack, output_config, round_config) print("\r\nShuffling input videos for round #%i..." % (r_i + 1)) if output_config.versions > 1: # Await previous cutter, if still previewing cutter_lock.acquire() # TODO: pass in cutter lock to release on preview exit clips = cutter.get_compilation() cutter_lock.release() else: clips = cutter.get_compilation() # Assemble audio from music and beats audio = None if (round_config.music is not None or round_config.beats is not None): audio = [ stack.enter_context(AudioFileClip(clip)) for clip in [ round_config.beats, round_config.music, ] if clip is not None ] # Concatenate this round's video clips together and add audio round_video = concatenate_videoclips(clips) if audio is not None: beat_audio = CompositeAudioClip(audio) level = round_config.audio_level if level > 1: beat_audio = volumex(beat_audio, 1 / level) else: round_video = volumex(round_video, level) audio = CompositeAudioClip([beat_audio, round_video.audio]) round_video = round_video.set_audio(audio) # Add beatmeter, if supplied if round_config.beatmeter is not None: # Wait for beatmeter, if it exists print("\r\nWaiting for beatmeter #%i..." % (r_i + 1)) beatmeter = beatmeter_thread.join() round_video = CompositeVideoClip([round_video, beatmeter]) round_video = round_video.set_duration(round_config.duration) # Fade in and out round_video = crossfade([ get_black_clip((output_config.xdim, output_config.ydim)), round_video, get_black_clip((output_config.xdim, output_config.ydim)), ]) if output_config.cache == "round": # Save each round video to disk filename = get_round_name(output_config.name, round_config.name, ext) filename = _write_video(stack, round_video, filename, codec, output_config.fps, ext) round_config._is_on_disk = filename is not None max_threads_semaphore.release() return filename else: # output_config.cache == "all": max_threads_semaphore.release() return round_video # Store round in memory instead
def concatenate(clipslist, transition=None, bg_color=(0, 0, 0), transparent=False, ismask=False, padding=0): """ Concatenates several video clips Returns a video clip made by clip by concatenating several video clips. (Concatenated means that they will be played one after another). if the clips do not have the same resolution, the final resolution will be such that no clip has to be resized. As a consequence the final clip has the height of the highest clip and the width of the widest clip of the list. All the clips with smaller dimensions will appear centered. The border will be transparent if mask=True, else it will be of the color specified by ``bg_color``. Returns a VideoClip instance if all clips have the same size and there is no transition, else a composite clip. Parameters ----------- clipslist A list of video clips which must all have their ``duration`` attributes set. transition A clip that will be played between each two clips of the list. bg_color Color of the background, if any. transparent If True, the resulting clip's mask will be the concatenation of the masks of the clips in the list. If the clips do not have the same resolution, the border around the smaller clips will be transparent. padding Duration during two consecutive clips. If negative, a clip will play at the same time as the clip it follows. A non-null padding automatically sets the method to `compose`. """ if transition != None: l = [[v, transition] for v in clipslist[:-1]] clipslist = reduce(lambda x, y: x + y, l) + [clipslist[-1]] transition = None tt = np.cumsum([0] + [c.duration for c in clipslist]) sizes = [v.size for v in clipslist] w = max([r[0] for r in sizes]) h = max([r[1] for r in sizes]) tt = np.maximum(0, tt + padding * np.arange(len(tt))) result = CompositeVideoClip( [c.set_start(t).set_pos('center') for (c, t) in zip(clipslist, tt)], size=(w, h), bg_color=bg_color, ismask=ismask, transparent=transparent) result.tt = tt result.clipslist = clipslist result.start_times = tt[:-1] result.start, result.duration, result.end = 0, tt[-1], tt[-1] audio_t = [(c.audio, t) for c, t in zip(clipslist, tt) if c.audio != None] if len(audio_t) > 0: result.audio = CompositeAudioClip([a.set_start(t) for a, t in audio_t]) return result
class CompositeVideoClip(VideoClip): """ A VideoClip made of other videoclips displayed together. This is the base class for most compositions. Parameters ---------- size The size (width, height) of the final clip. clips A list of videoclips. Clips with a higher ``layer`` attribute will be dislayed on top of other clips in a lower layer. If two or more clips share the same ``layer``, then the one appearing latest in ``clips`` will be displayed on top (i.e. it has the higher layer). For each clip: - The attribute ``pos`` determines where the clip is placed. See ``VideoClip.set_pos`` - The mask of the clip determines which parts are visible. Finally, if all the clips in the list have their ``duration`` attribute set, then the duration of the composite video clip is computed automatically bg_color Color for the unmasked and unfilled regions. Set to None for these regions to be transparent (will be slower). use_bgclip Set to True if the first clip in the list should be used as the 'background' on which all other clips are blitted. That first clip must have the same size as the final clip. If it has no transparency, the final clip will have no mask. The clip with the highest FPS will be the FPS of the composite clip. """ def __init__( self, clips, size=None, bg_color=None, use_bgclip=False, is_mask=False ): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = bg_color is None if bg_color is None: bg_color = 0.0 if is_mask else (0, 0, 0) fpss = [clip.fps for clip in clips if getattr(clip, "fps", None)] self.fps = max(fpss) if fpss else None VideoClip.__init__(self) self.size = size self.is_mask = is_mask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, color=self.bg_color, is_mask=is_mask) self.created_bg = True # order self.clips by layer self.clips = sorted(self.clips, key=lambda clip: clip.layer) # compute duration ends = [clip.end for clip in self.clips] if None not in ends: duration = max(ends) self.duration = duration self.end = duration # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if audioclips: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (clip.mask if (clip.mask is not None) else clip.add_mask().mask) .with_position(clip.pos) .with_end(clip.end) .with_start(clip.start, change_end=False) .with_layer(clip.layer) for clip in self.clips ] self.mask = CompositeVideoClip( maskclips, self.size, is_mask=True, bg_color=0.0 ) def make_frame(self, t): """The clips playing at time `t` are blitted over one another.""" frame = self.bg.get_frame(t).astype("uint8") im = Image.fromarray(frame) if self.bg.mask is not None: frame_mask = self.bg.mask.get_frame(t) im_mask = Image.fromarray(255 * frame_mask).convert("L") im = im.putalpha(im_mask) for clip in self.playing_clips(t): im = clip.blit_on(im, t) return np.array(im) def playing_clips(self, t=0): """Returns a list of the clips in the composite clips that are actually playing at the given time `t`. """ return [clip for clip in self.clips if clip.is_playing(t)] def close(self): """Closes the instance, releasing all the resources.""" if self.created_bg and self.bg: # Only close the background clip if it was locally created. # Otherwise, it remains the job of whoever created it. self.bg.close() self.bg = None if hasattr(self, "audio") and self.audio: self.audio.close() self.audio = None
def concatenate_videoclips( clips, method="chain", transition=None, bg_color=None, is_mask=False, padding=0 ): """Concatenates several video clips Returns a video clip made by clip by concatenating several video clips. (Concatenated means that they will be played one after another). There are two methods: - method="chain": will produce a clip that simply outputs the frames of the succesive clips, without any correction if they are not of the same size of anything. If none of the clips have masks the resulting clip has no mask, else the mask is a concatenation of masks (using completely opaque for clips that don't have masks, obviously). If you have clips of different size and you want to write directly the result of the concatenation to a file, use the method "compose" instead. - method="compose", if the clips do not have the same resolution, the final resolution will be such that no clip has to be resized. As a consequence the final clip has the height of the highest clip and the width of the widest clip of the list. All the clips with smaller dimensions will appear centered. The border will be transparent if mask=True, else it will be of the color specified by ``bg_color``. The clip with the highest FPS will be the FPS of the result clip. Parameters ----------- clips A list of video clips which must all have their ``duration`` attributes set. method "chain" or "compose": see above. transition A clip that will be played between each two clips of the list. bg_color Only for method='compose'. Color of the background. Set to None for a transparent clip padding Only for method='compose'. Duration during two consecutive clips. Note that for negative padding, a clip will partly play at the same time as the clip it follows (negative padding is cool for clips who fade in on one another). A non-null padding automatically sets the method to `compose`. """ if transition is not None: clip_transition_pairs = [[v, transition] for v in clips[:-1]] clips = reduce(lambda x, y: x + y, clip_transition_pairs) + [clips[-1]] transition = None timings = np.cumsum([0] + [clip.duration for clip in clips]) sizes = [clip.size for clip in clips] w = max(size[0] for size in sizes) h = max(size[1] for size in sizes) timings = np.maximum(0, timings + padding * np.arange(len(timings))) timings[-1] -= padding # Last element is the duration of the whole if method == "chain": def make_frame(t): i = max([i for i, e in enumerate(timings) if e <= t]) return clips[i].get_frame(t - timings[i]) def get_mask(clip): mask = clip.mask or ColorClip([1, 1], color=1, is_mask=True) if mask.duration is None: mask.duration = clip.duration return mask result = VideoClip(is_mask=is_mask, make_frame=make_frame) if any([clip.mask is not None for clip in clips]): masks = [get_mask(clip) for clip in clips] result.mask = concatenate_videoclips(masks, method="chain", is_mask=True) result.clips = clips elif method == "compose": result = CompositeVideoClip( [ clip.with_start(t).with_position("center") for (clip, t) in zip(clips, timings) ], size=(w, h), bg_color=bg_color, is_mask=is_mask, ) else: raise Exception( "Moviepy Error: The 'method' argument of " "concatenate_videoclips must be 'chain' or 'compose'" ) result.timings = timings result.start_times = timings[:-1] result.start, result.duration, result.end = 0, timings[-1], timings[-1] audio_t = [ (clip.audio, t) for clip, t in zip(clips, timings) if clip.audio is not None ] if audio_t: result.audio = CompositeAudioClip([a.with_start(t) for a, t in audio_t]) fpss = [clip.fps for clip in clips if getattr(clip, "fps", None) is not None] result.fps = max(fpss) if fpss else None return result
class CompositeVideoClip(VideoClip): """ A VideoClip made of other videoclips displayed together. This is the base class for most compositions. Parameters ---------- size The size (height x width) of the final clip. clips A list of videoclips. Each clip of the list will be displayed below the clips appearing after it in the list. For each clip: - The attribute ``pos`` determines where the clip is placed. See ``VideoClip.set_pos`` - The mask of the clip determines which parts are visible. Finally, if all the clips in the list have their ``duration`` attribute set, then the duration of the composite video clip is computed automatically bg_color Color for the unmasked and unfilled regions. Set to None for these regions to be transparent (will be slower). use_bgclip Set to True if the first clip in the list should be used as the 'background' on which all other clips are blitted. That first clip must have the same size as the final clip. If it has no transparency, the final clip will have no mask. The clip with the highest FPS will be the FPS of the composite clip. """ def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = bg_color is None if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [c.fps for c in clips if getattr(c, "fps", None)] self.fps = max(fpss) if fpss else None VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, color=self.bg_color, ismask=ismask) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if None not in ends: duration = max(ends) self.duration = duration self.end = duration # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if audioclips: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (c.mask if (c.mask is not None) else c.add_mask().mask) .set_position(c.pos) .set_end(c.end) .set_start(c.start, change_end=False) for c in self.clips ] self.mask = CompositeVideoClip( maskclips, self.size, ismask=True, bg_color=0.0 ) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = self.bg.get_frame(t) for c in self.playing_clips(t): f = c.blit_on(f, t) return f self.make_frame = make_frame def playing_clips(self, t=0): """ Returns a list of the clips in the composite clips that are actually playing at the given time `t`. """ return [c for c in self.clips if c.is_playing(t)] def close(self): if self.created_bg and self.bg: # Only close the background clip if it was locally created. # Otherwise, it remains the job of whoever created it. self.bg.close() self.bg = None if hasattr(self, "audio") and self.audio: self.audio.close() self.audio = None
class CompositeVideoClip(VideoClip): """ A VideoClip made of other videoclips displayed together. This is the base class for most compositions. Parameters ---------- size The size (height x width) of the final clip. clips A list of videoclips. Each clip of the list will be displayed below the clips appearing after it in the list. For each clip: - The attribute ``pos`` determines where the clip is placed. See ``VideoClip.set_pos`` - The mask of the clip determines which parts are visible. Finally, if all the clips in the list have their ``duration`` attribute set, then the duration of the composite video clip is computed automatically bg_color Color for the unmasked and unfilled regions. Set to None for these regions to be transparent (will be slower). use_bgclip Set to True if the first clip in the list should be used as the 'background' on which all other clips are blitted. That first clip must have the same size as the final clip. If it has no transparency, the final clip will have no mask. The clip with the highest FPS will be the FPS of the composite clip. """ def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = (bg_color is None) if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [c.fps for c in clips if hasattr(c, 'fps') and c.fps is not None] if len(fpss) == 0: self.fps = None else: self.fps = max(fpss) VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, col=self.bg_color) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if not any([(e is None) for e in ends]): self.duration = max(ends) self.end = max(ends) # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if len(audioclips) > 0: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [(c.mask if (c.mask is not None) else c.add_mask().mask).set_pos(c.pos).set_end(c.end).set_start(c.start, change_end=False) for c in self.clips] self.mask = CompositeVideoClip(maskclips,self.size, ismask=True, bg_color=0.0) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = self.bg.get_frame(t) for c in self.playing_clips(t): f = c.blit_on(f, t) return f self.make_frame = make_frame def playing_clips(self, t=0): """ Returns a list of the clips in the composite clips that are actually playing at the given time `t`. """ return [c for c in self.clips if c.is_playing(t)] def close(self): if self.created_bg and self.bg: # Only close the background clip if it was locally created. # Otherwise, it remains the job of whoever created it. self.bg.close() self.bg = None if hasattr(self, "audio") and self.audio: self.audio.close() self.audio = None
def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = (bg_color is None) if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [c.fps for c in clips if hasattr(c, 'fps') and c.fps is not None] if len(fpss) == 0: self.fps = None else: self.fps = max(fpss) VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, col=self.bg_color) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if not any([(e is None) for e in ends]): self.duration = max(ends) self.end = max(ends) # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if len(audioclips) > 0: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [(c.mask if (c.mask is not None) else c.add_mask().mask).set_pos(c.pos).set_end(c.end).set_start(c.start, change_end=False) for c in self.clips] self.mask = CompositeVideoClip(maskclips,self.size, ismask=True, bg_color=0.0) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = self.bg.get_frame(t) for c in self.playing_clips(t): f = c.blit_on(f, t) return f self.make_frame = make_frame
def concatenate(clipslist, method = 'chain', transition=None, bg_color=(0, 0, 0), transparent=False, ismask=False, crossover = 0): """ Concatenates several video clips Returns a video clip made by clip by concatenating several video clips. (Concatenated means that they will be played one after another). if the clips do not have the same resolution, the final resolution will be such that no clip has to be resized. As a consequence the final clip has the height of the highest clip and the width of the widest clip of the list. All the clips with smaller dimensions will appear centered. The border will be transparent if mask=True, else it will be of the color specified by ``bg_color``. Returns a VideoClip instance if all clips have the same size and there is no transition, else a composite clip. Parameters ----------- clipslist A list of video clips which must all have their ``duration`` attributes set. transition A clip that will be played between each two clips of the list. bg_color Color of the background, if any. transparent If True, the resulting clip's mask will be the concatenation of the masks of the clips in the list. If the clips do not have the same resolution, the border around the smaller clips will be transparent. """ if transition != None: l = [[v, transition] for v in clipslist[:-1]] clipslist = reduce(lambda x, y: x + y, l) + [clipslist[-1]] transition = None tt = np.cumsum([0] + [c.duration for c in clipslist]) sizes = [v.size for v in clipslist] w = max([r[0] for r in sizes]) h = max([r[1] for r in sizes]) if method == 'chain': result = VideoClip(ismask = ismask) result.size = (w,h) def gf(t): i = max([i for i, e in enumerate(tt) if e <= t]) return clipslist[i].get_frame(t - tt[i]) result.get_frame = gf if (len(set(map(tuple,sizes)))>1) and (bg_color is not None): # If not all clips have the same size, flatten the result # on some color result = result.fx( on_color, (w,h), bg_color, 'center') elif method == 'compose': tt = np.maximum(0, tt - crossover*np.arange(len(tt))) result = concatenate( [c.set_start(t).set_pos('center') for (c, t) in zip(clipslist, tt)], size = (w, h), bg_color=bg_color, ismask=ismask, transparent=transparent) result.tt = tt result.clipslist = clipslist result.start_times = tt[:-1] result.start, result.duration, result.end = 0, tt[-1] , tt[-1] # Compute the mask if any if transparent and (not ismask): # add a mask to the clips which have none clips_withmask = [(c if (c.mask!=None) else c.add_mask()) for c in clipslist] result.mask = concatenate([c.mask for c in clips_withmask], bg_color=0, ismask=True, transparent=False) # Compute the audio, if any. audio_t = [(c.audio,t) for c,t in zip(clipslist,tt) if c.audio!=None] if len(audio_t)>0: result.audio = CompositeAudioClip([a.set_start(t) for a,t in audio_t]) return result
def __init__(self, clips, size=None, bg_color=None, use_bgclip=True, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = (bg_color is None) if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [c.fps for c in clips if getattr(c, 'fps', None)] self.fps = max(fpss) if fpss else None VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, color=self.bg_color) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if None not in ends: duration = max(ends) self.duration = duration self.end = duration # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if audioclips: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (c.mask if (c.mask is not None) else c.add_mask().mask).set_position( c.pos).set_end(c.end).set_start(c.start, change_end=False) for c in self.clips ] self.mask = CompositeVideoClip(maskclips, self.size, ismask=True, bg_color=0.0) def make_frame(t): full_w, full_h = self.bg.size f = self.bg.get_frame(t) bg_im = Image.fromarray(f) for c in self.playing_clips(t): img, pos, mask, ismask = c.new_blit_on(t, f) x, y = pos w, h = c.size out_x = x < -w or x == full_w out_y = y < -h or y == full_h if out_x and out_y: continue pos = (int(round(min(max(-w, x), full_w))), int(round(min(max(-h, y), full_h)))) paste_im = Image.fromarray(img) if mask is not None: mask_im = Image.fromarray(255 * mask).convert('L') bg_im.paste(paste_im, pos, mask_im) else: bg_im.paste(paste_im, pos) result_frame = np.array(bg_im) return result_frame.astype('uint8') if ( not ismask) else result_frame self.make_frame = make_frame
def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = bg_color is None if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [c.fps for c in clips if getattr(c, "fps", None)] self.fps = max(fpss) if fpss else None VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, color=self.bg_color, ismask=ismask) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if None not in ends: duration = max(ends) self.duration = duration self.end = duration # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if audioclips: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (c.mask if (c.mask is not None) else c.add_mask().mask) .set_position(c.pos) .set_end(c.end) .set_start(c.start, change_end=False) for c in self.clips ] self.mask = CompositeVideoClip( maskclips, self.size, ismask=True, bg_color=0.0 ) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = self.bg.get_frame(t) for c in self.playing_clips(t): f = c.blit_on(f, t) return f self.make_frame = make_frame
def __init__( self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False, allow_no_bg=False, ): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = bg_color is None if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [ c.fps for c in clips if hasattr(c, "fps") and c.fps is not None ] if len(fpss) == 0: self.fps = None else: self.fps = max(fpss) VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color self.created_bg = False if use_bgclip: self.bg = clips[0] self.clips = clips[1:] elif allow_no_bg: # In some cases if we are rendering images, # we allow no bottom BG self.clips = clips self.bg = None else: self.bg = ColorClip(size, color=self.bg_color) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if not any([(e is None) for e in ends]): self.duration = max(ends) self.end = max(ends) # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if len(audioclips) > 0: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (c.mask if (c.mask is not None) else c.add_mask().mask).set_position( c.pos).set_end(c.end).set_start(c.start, change_end=False) for c in self.clips ] self.mask = CompositeVideoClip(maskclips, self.size, ismask=True, bg_color=0.0) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = None if self.bg is None else self.bg.get_frame(t) for c in self.playing_clips(t): if f is None: f = c.get_frame(t) else: f = c.blit_on(f, t) return f self.make_frame = make_frame
def concatenate_videoclips(clips, method="chain", transition=None, bg_color=None, ismask=False, padding=0): """ Concatenates several video clips Returns a video clip made by clip by concatenating several video clips. (Concatenated means that they will be played one after another). There are two methods: - method="chain": will produce a clip that simply outputs the frames of the succesive clips, without any correction if they are not of the same size of anything. If none of the clips have masks the resulting clip has no mask, else the mask is a concatenation of masks (using completely opaque for clips that don't have masks, obviously). If you have clips of different size and you want to write directly the result of the concatenation to a file, use the method "compose" instead. - method="compose", if the clips do not have the same resolution, the final resolution will be such that no clip has to be resized. As a consequence the final clip has the height of the highest clip and the width of the widest clip of the list. All the clips with smaller dimensions will appear centered. The border will be transparent if mask=True, else it will be of the color specified by ``bg_color``. If all clips with a fps attribute have the same fps, it becomes the fps of the result. Parameters ----------- clips A list of video clips which must all have their ``duration`` attributes set. method "chain" or "compose": see above. transition A clip that will be played between each two clips of the list. bg_color Only for method='compose'. Color of the background. Set to None for a transparent clip padding Only for method='compose'. Duration during two consecutive clips. Note that for negative padding, a clip will partly play at the same time as the clip it follows (negative padding is cool for clips who fade in on one another). A non-null padding automatically sets the method to `compose`. """ if transition is not None: l = [[v, transition] for v in clips[:-1]] clips = reduce(lambda x, y: x + y, l) + [clips[-1]] transition = None tt = np.cumsum([0] + [c.duration for c in clips]) sizes = [v.size for v in clips] w = max([r[0] for r in sizes]) h = max([r[1] for r in sizes]) tt = np.maximum(0, tt + padding * np.arange(len(tt))) if method == "chain": def make_frame(t): i = max([i for i, e in enumerate(tt) if e <= t]) return clips[i].get_frame(t - tt[i]) result = VideoClip(ismask=ismask, make_frame=make_frame) if any([c.mask is not None for c in clips]): masks = [ c.mask if (c.mask is not None) else ColorClip( [1, 1], col=1, ismask=True, duration=c.duration) #ColorClip(c.size, col=1, ismask=True).set_duration(c.duration) for c in clips ] result.mask = concatenate_videoclips(masks, method="chain", ismask=True) result.clips = clips elif method == "compose": result = CompositeVideoClip( [c.set_start(t).set_pos('center') for (c, t) in zip(clips, tt)], size=(w, h), bg_color=bg_color, ismask=ismask) result.tt = tt result.start_times = tt[:-1] result.start, result.duration, result.end = 0, tt[-1], tt[-1] audio_t = [(c.audio, t) for c, t in zip(clips, tt) if c.audio is not None] if len(audio_t) > 0: result.audio = CompositeAudioClip([a.set_start(t) for a, t in audio_t]) fps_list = list(set([c.fps for c in clips if hasattr(c, 'fps')])) if len(fps_list) == 1: result.fps = fps_list[0] return result
def __init__( self, clips, size=None, bg_color=None, use_bgclip=False, is_mask=False ): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = bg_color is None if bg_color is None: bg_color = 0.0 if is_mask else (0, 0, 0) fpss = [clip.fps for clip in clips if getattr(clip, "fps", None)] self.fps = max(fpss) if fpss else None VideoClip.__init__(self) self.size = size self.is_mask = is_mask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, color=self.bg_color, is_mask=is_mask) self.created_bg = True # order self.clips by layer self.clips = sorted(self.clips, key=lambda clip: clip.layer) # compute duration ends = [clip.end for clip in self.clips] if None not in ends: duration = max(ends) self.duration = duration self.end = duration # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if audioclips: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (clip.mask if (clip.mask is not None) else clip.add_mask().mask) .with_position(clip.pos) .with_end(clip.end) .with_start(clip.start, change_end=False) .with_layer(clip.layer) for clip in self.clips ] self.mask = CompositeVideoClip( maskclips, self.size, is_mask=True, bg_color=0.0 )
class CompositeVideoClip(VideoClip): """ A VideoClip made of other videoclips displayed together. This is the base class for most compositions. Parameters ---------- size The size (height x width) of the final clip. clips A list of videoclips. Each clip of the list will be displayed below the clips appearing after it in the list. For each clip: - The attribute ``pos`` determines where the clip is placed. See ``VideoClip.set_pos`` - The mask of the clip determines which parts are visible. Finally, if all the clips in the list have their ``duration`` attribute set, then the duration of the composite video clip is computed automatically bg_color Color for the unmasked and unfilled regions. Set to None for these regions to be transparent (will be slower). use_bgclip Set to True if the first clip in the list should be used as the 'background' on which all other clips are blitted. That first clip must have the same size as the final clip. If it has no transparency, the final clip will have no mask. The clip with the highest FPS will be the FPS of the composite clip. """ def __init__(self, clips, size=None, bg_color=None, use_bgclip=True, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = (bg_color is None) if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fpss = [c.fps for c in clips if getattr(c, 'fps', None)] self.fps = max(fpss) if fpss else None VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] self.created_bg = False else: self.clips = clips self.bg = ColorClip(size, color=self.bg_color) self.created_bg = True # compute duration ends = [c.end for c in self.clips] if None not in ends: duration = max(ends) self.duration = duration self.end = duration # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if audioclips: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (c.mask if (c.mask is not None) else c.add_mask().mask).set_position( c.pos).set_end(c.end).set_start(c.start, change_end=False) for c in self.clips ] self.mask = CompositeVideoClip(maskclips, self.size, ismask=True, bg_color=0.0) def make_frame(t): full_w, full_h = self.bg.size f = self.bg.get_frame(t) bg_im = Image.fromarray(f) for c in self.playing_clips(t): img, pos, mask, ismask = c.new_blit_on(t, f) x, y = pos w, h = c.size out_x = x < -w or x == full_w out_y = y < -h or y == full_h if out_x and out_y: continue pos = (int(round(min(max(-w, x), full_w))), int(round(min(max(-h, y), full_h)))) paste_im = Image.fromarray(img) if mask is not None: mask_im = Image.fromarray(255 * mask).convert('L') bg_im.paste(paste_im, pos, mask_im) else: bg_im.paste(paste_im, pos) result_frame = np.array(bg_im) return result_frame.astype('uint8') if ( not ismask) else result_frame self.make_frame = make_frame def playing_clips(self, t=0): """ Returns a list of the clips in the composite clips that are actually playing at the given time `t`. """ return [c for c in self.clips if c.is_playing(t)] def close(self): if self.created_bg and self.bg: # Only close the background clip if it was locally created. # Otherwise, it remains the job of whoever created it. self.bg.close() self.bg = None if hasattr(self, "audio") and self.audio: self.audio.close() self.audio = None
def __init__(self, clips, size=None, bg_color=None, use_bgclip=False, ismask=False): if size is None: size = clips[0].size if use_bgclip and (clips[0].mask is None): transparent = False else: transparent = (bg_color is None) if bg_color is None: bg_color = 0.0 if ismask else (0, 0, 0) fps_list = list(set([c.fps for c in clips if hasattr(c, 'fps')])) if len(fps_list) == 1: self.fps = fps_list[0] VideoClip.__init__(self) self.size = size self.ismask = ismask self.clips = clips self.bg_color = bg_color if use_bgclip: self.bg = clips[0] self.clips = clips[1:] else: self.clips = clips self.bg = ColorClip(size, col=self.bg_color) # compute duration ends = [c.end for c in self.clips] if not any([(e is None) for e in ends]): self.duration = max(ends) self.end = max(ends) # compute audio audioclips = [v.audio for v in self.clips if v.audio is not None] if len(audioclips) > 0: self.audio = CompositeAudioClip(audioclips) # compute mask if necessary if transparent: maskclips = [ (c.mask if (c.mask is not None) else c.add_mask().mask).set_pos(c.pos) for c in self.clips ] self.mask = CompositeVideoClip(maskclips, self.size, ismask=True, bg_color=0.0) def make_frame(t): """ The clips playing at time `t` are blitted over one another. """ f = self.bg.get_frame(t) for c in self.playing_clips(t): f = c.blit_on(f, t) return f self.make_frame = make_frame