Ejemplo n.º 1
0
def process_vid(video, args):
    """
    If video is not valid we will print error and pass
    out_directory must exist!
    """

    out_directory = get_output_directory(args.outdir, video, args)

    try:
        clip = VideoFileClip(video)
    except Exception as e:
        print(f"Unable to open clip{video}: {e}")
        return

    save_file = get_save_file(out_directory, video, args)
    if args.time_start is not None:
        clip = clip.subclip(t_start=args.time_start, t_end=args.time_end)

    if exists(save_file):
        scenes = FramesMatches.load(save_file)
    else:
        scenes = FramesMatches.from_clip(clip.resize(width=120), dist_thr=args.dist_thr, max_d=args.max_d)
        try:
            scenes.save(save_file)
        except Exception as e:
            print(f"Unable to save matches: {e}")

    selected_scenes = scenes.select_scenes(match_thr=args.match_thr, min_time_span=args.min_time_span,
                                           nomatch_thr=args.nomatch_thr, time_distance=args.time_distance)
    selected_scenes.write_gifs(clip.resize(width=450), out_directory)
    optimize_dir(out_directory)
Ejemplo n.º 2
0
def test_FramesMatches_best(n, percent, expected_result):
    assert (FramesMatches([
        FramesMatch(1, 2, 0, 0),
        FramesMatch(2, 3, 0, 0),
        FramesMatch(4, 5, 0, 0),
        FramesMatch(5, 6, 0, 0),
    ]).best(n=n, percent=percent) == expected_result)
Ejemplo n.º 3
0
def saveVideoSummaryAsGIF(destinationDirectory, file_path, image_width):
    # Open a video file (any format should work)
    clip = VideoFileClip(file_path)

    matches = FramesMatches.from_clip(clip, 1, 1)  # loose matching

    # find the best matching pair of frames > 1.5s away
    # best = matches.filter(lambda x: x.time_span > 0.5).best()
    # Write the sequence to a GIF (with speed=30% of the original)
    final = clip.subclip(matches[0].t1, matches[0].t2).speedx(0.3)
    final.write_gif(destinationDirectory, fps=2)
Ejemplo n.º 4
0
def test_FramesMatches_save_load(util):
    input_matching_frames = [
        FramesMatch(1, 2, 0, 0),
        FramesMatch(1, 2, 0.8, 0),
        FramesMatch(1, 2, 0.8, 0.8),
    ]
    expected_frames_matches_file_content = """1.000	2.000	0.000	0.000
1.000	2.000	0.800	0.000
1.000	2.000	0.800	0.800
"""

    outputfile = os.path.join(util.TMP_DIR,
                              "moviepy_FramesMatches_save_load.txt")

    # save
    FramesMatches(input_matching_frames).save(outputfile)

    with open(outputfile, "r") as f:
        assert f.read() == expected_frames_matches_file_content

    # load
    for i, frames_match in enumerate(FramesMatches.load(outputfile)):
        assert frames_match == input_matching_frames[i]
Ejemplo n.º 5
0
def test_FramesMatches_filter():
    input_matching_frames = [
        FramesMatch(1, 2, 0, 0),
        FramesMatch(1, 2, 0.8, 0.8),
        FramesMatch(1, 2, 0.8, 0),
    ]
    expected_matching_frames = [FramesMatch(1, 2, 0, 0)]
    matching_frames_filter = lambda x: not x.min_distance and not x.max_distance

    matching_frames = FramesMatches(input_matching_frames).filter(
        matching_frames_filter)

    assert len(matching_frames) == len(expected_matching_frames)
    for i, frames_match in enumerate(matching_frames):
        assert frames_match == expected_matching_frames[i]
Ejemplo n.º 6
0
def process_vid(video, out_directory):
    """
    If video is not valid we will print error and pass
    out_directory must exist!
    """
    try:
        clip = mp.VideoFileClip(video)
        scenes = FramesMatches.from_clip(clip.resize(width=120), 10, 3)
    except Exception:
        print("oops, Looks like {} isn't a vid".format(video))
        return
    # Cinderalla
    # match_thr=2, min_time_span=0.5, nomatch_thr=4, time_distance=0.5
    # Ex
    # match_thr=1, min_time_span=1.5, nomatch_thr=2, time_distance=0.5)
    selected_scenes = scenes.select_scenes(2, 1, 4, 0.5)
    selected_scenes.write_gifs(clip.resize(width=450), out_directory)
Ejemplo n.º 7
0
def test_FramesMatches_select_scenes(
    filename,
    subclip,
    match_threshold,
    min_time_span,
    nomatch_threshold,
    expected_result,
):
    video_clip = VideoFileClip(filename)
    if subclip is not None:
        video_clip = video_clip.subclip(subclip[0], subclip[1])
    clip = concatenate_videoclips([video_clip.fx(time_mirror), video_clip])
    result = FramesMatches.from_clip(clip, 10, 3, logger=None).select_scenes(
        match_threshold,
        min_time_span,
        nomatch_threshold=nomatch_threshold,
    )

    assert len(result) == len(expected_result)
    assert result == expected_result
Ejemplo n.º 8
0
def test_FramesMatches_from_clip(
    bitmap,
    expected_matches,
    distance_threshold,
    max_duration,
):
    clip = BitmapClip(bitmap, fps=1)

    matching_frames = FramesMatches.from_clip(
        clip,
        distance_threshold,
        max_duration,
        logger=None,
    )

    assert matching_frames
    assert isinstance(matching_frames, FramesMatches)
    assert isinstance(matching_frames[0], FramesMatch)

    for i, match in enumerate(matching_frames):
        for j, n in enumerate(match):
            assert round(n, 4) == expected_matches[i][j]
Ejemplo n.º 9
0
def test_FramesMatches_write_gifs(util):
    video_clip = VideoFileClip("media/chaplin.mp4").subclip(0, 0.2)
    clip = concatenate_videoclips([video_clip.fx(time_mirror), video_clip])

    # add matching frame starting at start < clip.start which should be ignored
    matching_frames = FramesMatches.from_clip(clip, 10, 3, logger=None)
    matching_frames.insert(0, FramesMatch(-1, -0.5, 0, 0))
    matching_frames = matching_frames.select_scenes(
        1,
        0.01,
        nomatch_threshold=0,
    )

    gifs_dir = os.path.join(util.TMP_DIR, "moviepy_FramesMatches_write_gifs")
    if os.path.isdir(gifs_dir):
        shutil.rmtree(gifs_dir)
    os.mkdir(gifs_dir)
    assert os.path.isdir(gifs_dir)

    matching_frames.write_gifs(clip, gifs_dir, logger=None)

    gifs_filenames = os.listdir(gifs_dir)
    assert len(gifs_filenames) == 7

    for filename in gifs_filenames:
        filepath = os.path.join(gifs_dir, filename)
        assert os.path.isfile(filepath)

        with open(filepath, "rb") as f:
            assert len(f.readline())

        end, start = filename.split(".")[0].split("_")
        end, start = (int(end), int(start))
        assert isinstance(end, int)
        assert isinstance(end, int)

    shutil.rmtree(gifs_dir)
Ejemplo n.º 10
0
def loopDetection(videoId):
	print "////////////////"
	print "Looking for loops..."

	outputDir = os.path.join(_STATIC_BASE, videoId)
	selected_scenes_file = os.path.join(outputDir, "loops.txt") #ultimately what we want to fill
	if not os.path.exists(selected_scenes_file):

		videoFile = getVideoPath(videoId)
		clip = VideoFileClip(videoFile, audio=False)
		
		clip_small = clip.resize(width=150) # Downsize the clip to a width of 150px to speed up things

		matches = FramesMatches.from_clip(clip_small, 5, 3) # Find all the pairs of matching frames an return their corresponding start and end times.
		# matchesFile = os.path.join(outputDir, "matches.txt") # (Optional) Save the matches for later use. 
		# matches.save(matchesFile)
		# matches = FramesMatches.load(matchesFile)

		# Filter the scenes: keep only segments with duration >1.5 seconds,
		# where the first and last frame have a per-pixel distance < 1,
		# with at least one frame at a distance 2 of the first frame,
		# and with >0.5 seconds between the starts of the selected segments.
		selected_scenes = matches.select_scenes(match_thr=10, min_time_span=1.5, nomatch_thr=.5, time_distance=1)

		print " ______ loops... ______ " #if any
		selected_scenes.save(selected_scenes_file) #save selected scenes

	scenes = []
	ss = open(selected_scenes_file, "r")
	for line in ss:
		start, end, c, d = line.split("\t")
		scene = {
			'start': start,
			'end': end
		}
		scenes.append(scene)
	return scenes
Ejemplo n.º 11
0
def from_clip(clip, dist_thr, max_d, fps=None, dark_mean=100, dark_std=10):
    """ Finds all the frames tht look alike in a clip, for instance to make a
    looping gif.
    This teturns a  FramesMatches object of the all pairs of frames with
    (t2-t1 < max_d) and whose distance is under dist_thr.
    This is well optimized routine and quite fast.
    Examples
    ---------
    
    We find all matching frames in a given video and turn the best match with
    a duration of 1.5s or more into a GIF:
    >>> from moviepy.editor import VideoFileClip
    >>> from moviepy.video.tools.cuts import find_matching_frames
    >>> clip = VideoFileClip("foo.mp4").resize(width=200)
    >>> matches = find_matching_frames(clip, 10, 3) # will take time
    >>> best = matches.filter(lambda m: m.time_span > 1.5).best()
    >>> clip.subclip(best.t1, best.t2).write_gif("foo.gif")
    Parameters
    -----------
    clip
      A MoviePy video clip, possibly transformed/resized
    
    dist_thr
      Distance above which a match is rejected
    
    max_d
      Maximal duration (in seconds) between two matching frames
    
    fps
      Frames per second (default will be clip.fps)
    
    """ 
    
    N_pixels = clip.w * clip.h * 3
    dot_product = lambda F1, F2: (F1*F2).sum()/N_pixels
    F = {} # will store the frames and their mutual distances
    
    def distance(t1, t2):
        uv = dot_product(F[t1]['frame'], F[t2]['frame'])
        u, v = F[t1]['|F|sq'], F[t2]['|F|sq']
        return np.sqrt(u+v - 2*uv)
    
    matching_frames = [] # the final result.
    
    for (t,frame) in clip.iter_frames(with_times=True, progress_bar=True):
        if frame.mean() <= dark_mean and frame.mean(2).std() <= dark_std:
            continue

        flat_frame = 1.0*frame.flatten()
        F_norm_sq = dot_product(flat_frame, flat_frame)
        F_norm = np.sqrt(F_norm_sq)

        for t2 in list(F.keys()):
            # forget old frames, add 't' to the others frames
            # check for early rejections based on differing norms
            if (t-t2) > max_d:
                F.pop(t2)
            else:
                F[t2][t] = {'min':abs(F[t2]['|F|'] - F_norm),
                            'max':F[t2]['|F|'] + F_norm}
                F[t2][t]['rejected']= (F[t2][t]['min'] > dist_thr)
        
        t_F = sorted(F.keys())
        
        F[t] = {'frame': flat_frame, '|F|sq': F_norm_sq, '|F|': F_norm}
                
        for i,t2 in enumerate(t_F):
            # Compare F(t) to all the previous frames
            
            if F[t2][t]['rejected']:
                continue
            
            dist = distance(t, t2)
            F[t2][t]['min'] = F[t2][t]['max'] = dist
            F[t2][t]['rejected']  = (dist >= dist_thr)
            
            for t3 in t_F[i+1:]:
                # For all the next times t3, use d(F(t), F(t2)) to
                # update the bounds on d(F(t), F(t3)). See if you can
                # conclude on wether F(t) and F(t3) match.
                t3t, t2t3 = F[t3][t], F[t2][t3]
                t3t['max'] = min(t3t['max'], dist+ t2t3['max'])
                t3t['min'] = max(t3t['min'], dist - t2t3['max'],
                                 t2t3['min'] - dist)
                                      
                if t3t['min'] > dist_thr:
                    t3t['rejected'] = True
    
        # Store all the good matches (t2,t)
        matching_frames += [(t1, t, F[t1][t]['min'], F[t1][t]['max']) for t1 in F
                            if (t1!=t) and not F[t1][t]['rejected']]
                   
    return FramesMatches([FramesMatch(*e) for e in matching_frames])
Ejemplo n.º 12
0
    with open(outputfile, "r") as f:
        assert f.read() == expected_frames_matches_file_content

    # load
    for i, frames_match in enumerate(FramesMatches.load(outputfile)):
        assert frames_match == input_matching_frames[i]


@pytest.mark.parametrize(
    ("n", "percent", "expected_result"),
    (
        pytest.param(1, None, FramesMatch(1, 2, 0, 0), id="n=1"),
        pytest.param(
            2,
            None,
            FramesMatches([FramesMatch(1, 2, 0, 0),
                           FramesMatch(2, 3, 0, 0)]),
            id="n=2",
        ),
        pytest.param(
            1,
            50,
            FramesMatches([FramesMatch(1, 2, 0, 0),
                           FramesMatch(2, 3, 0, 0)]),
            id="percent=50",
        ),
    ),
)
def test_FramesMatches_best(n, percent, expected_result):
    assert (FramesMatches([
        FramesMatch(1, 2, 0, 0),
        FramesMatch(2, 3, 0, 0),
Ejemplo n.º 13
0
from moviepy.video.tools.cuts import FramesMatches
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
import datetime
import argparse
from subprocess import call
import img2stl

# clean up

# eg. python bin/process-video.py media/fish.mp4
parser = argparse.ArgumentParser(
    description='Extract seamless loops from a video')
parser.add_argument('file', metavar='N', type=str, help='vido file')
args = parser.parse_args()
clip = mpy.VideoFileClip(args.file).resize(width=200)
matches = FramesMatches.from_clip(clip, 40, 3)  # loose matching
# find the best matching pair of frames > 1.5s away
best = matches.filter(lambda x: x.time_span > 1.5).best()

# create clip of loop
start_time = datetime.timedelta(0, best.t1)
end_time = datetime.timedelta(0, best.t2)
duration = end_time - end_time

call(
    "ffmpeg -i {} -vcodec copy -acodec copy -ss {} -to {} tmp/clip.mp4".format(
        args.file, start_time, end_time),
    shell=True)

ffmpeg_extract_subclip(args.file, best.t1, best.t2, targetname="tmp/clip.mp4")