def test_filter_against(self): intrvl_long1 = Interval(1., 10., 1) intrvl_long2 = Interval(3., 15., 2) intrvlshort1 = Interval(2., 2.5, 3) intrvlshort2 = Interval(2., 2.7, 4) intrvlshort3 = Interval(2.9, 3.5, 5) intrvlslong = IntervalList([intrvl_long2, intrvl_long1]) intrvlsshort = IntervalList([intrvlshort1, intrvlshort2, intrvlshort3]) intrvlsfiltered = intrvlslong.filter_against(intrvlsshort, predicate=during_inv()) self.assertEqual(len(intrvlsfiltered.intrvls), 1) self.assertEqual(intrvlsfiltered.intrvls[0].__repr__(), intrvl_long1.__repr__())
# One interval for every frame whose payload is list of faces in that frame faces_at_boundaries = IntervalList([ (frame, frame, facelist) for frame, facelist in zip(frames, faces[0].load())) ]).filter_against( transitions, predicate=overlaps() ).filter(payload_satisfies(length_at_least(1))) # Get all boundaries where there are faces before and after the boundary boundaries_that_have_faces = transitions.filter_against( faces_at_boundaries, predicate=starts_inv() # Faces at the start of this transition ).filter_against( transitions.filter_against( faces_at_boundaries, predicate=finishes_inv() # Faces at the end of this transition ), predicate=equal() ) # Annotate boundary payloads with the faces at the start/end of each transition boundaries_starting_faces = boundaries_that_have_faces.merge( faces_at_boundaries, predicate = starts_inv(), payload_merge_op = payload_second ) boundaries_ending_faces = boundaries_that_have_faces.merge( faces_at_boundaries, predicate = finishes_inv(), payload_merge_op = payload_second ) # Finally, annotate boundary with a payload of the faces that start/end the
def compute_shots(microshot_boundaries, faces_scanner, frames, video): print('Number of microshots: ', len(microshot_boundaries)) faces_per_frame = IntervalList([ (frame, frame, facelist) for frame, facelist in zip(frames, faces_scanner) ]) transitions = IntervalList([(boundary - 1, boundary, 0) for boundary in microshot_boundaries]) faces_at_boundaries = faces_per_frame.filter_against( transitions, predicate=overlaps()).filter(payload_satisfies(length_at_least(1))) # Get all transitions where there are faces before and after the transition # This IntervalList's payload is stil 0 transitions_with_faces = transitions.filter_against( faces_at_boundaries, predicate=starts_inv()).filter_against( transitions.filter_against(faces_at_boundaries, predicate=finishes_inv()), predicate=equal()) # Annotate transitions_with_faces with the list of faces before and after # every transition transitions_with_faces_at_start_of_transition = transitions_with_faces.merge( faces_at_boundaries, predicate=starts_inv(), payload_merge_op=payload_second) transitions_with_faces_at_end_of_transition = transitions_with_faces.merge( faces_at_boundaries, predicate=finishes_inv(), payload_merge_op=payload_second) transitions_with_faces = transitions_with_faces_at_start_of_transition.merge( transitions_with_faces_at_end_of_transition, predicate=equal(), payload_merge_op=lambda starting_faces, ending_faces: { 'starts': starting_faces, 'finishes': ending_faces }) # Get all the transitions where the faces at the start and the end are # the same def face_list_stays_the_same(start_finishes_payload): """ Define a scene graph by the face positions at the start and check if the face positions at the end satisfy it. """ graph = { 'nodes': [{ 'name': 'face{}'.format(idx), 'predicates': [ position(face.x1, face.y1, face.x2, face.y2, epsilon=POSITION_EPSILON), lambda face: face['score'] > MINIMUM_FACE_PROBABILITY ] } for idx, face in enumerate(start_finishes_payload['starts']) if face.score > MINIMUM_FACE_PROBABILITY], 'edges': [] } return scene_graph(graph, exact=True)([{ 'x1': face.x1, 'y1': face.y1, 'x2': face.x2, 'y2': face.y2, 'score': face.score } for face in start_finishes_payload['finishes']]) bad_transitions = transitions_with_faces.filter( payload_satisfies(face_list_stays_the_same)) print(bad_transitions.size()) # Finally, compute shot boundaries def convert_shot_boundaries_to_shots(shot_boundary_list): """ Helper function to convert an IntervalList of shot boundaries to an IntervalList of shots. shot_boundary_list should have the start and end of the movie as boundaries. """ def fold_boundaries_to_shots(acc, frame): if acc == []: return [frame.copy()] top = acc[-1] top.end = frame.start - 1 if top.length() > 0: acc.append(frame.copy()) else: top.end = frame.start return acc return shot_boundary_list.fold_list(fold_boundaries_to_shots, []) # Convert microshot boundaries to IntervalList shot_boundaries = IntervalList([ (boundary, boundary, 0) for boundary in list(set([0, video.num_frames] + microshot_boundaries)) ]) microshots = convert_shot_boundaries_to_shots(shot_boundaries) # Filter out short microshots short_microshots = microshots.filter_length( max_length=math.floor(MINIMUM_SHOT_DURATION * video.fps)) shots = microshots.set_union( short_microshots.map( lambda i: (i.start, i.end + 1, i.payload)).coalesce()).coalesce() # Remove shots that start with the bad boundaries we found earlier bad_shots = shots.filter_against( bad_transitions.map(lambda i: (i.start + 1, i.end, i.payload)), predicate=starts_inv()) shot_boundaries = shots.map(lambda i: (i.start, i.start, i.payload)) shot_boundaries_without_bad_shots = shot_boundaries.minus(bad_shots) shots = convert_shot_boundaries_to_shots(shot_boundaries_without_bad_shots) return shots