예제 #1
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)
예제 #2
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]
예제 #3
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]
예제 #4
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)
예제 #5
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])
예제 #6
0
    # 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]


@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",
        ),
    ),