def make_cropped_video(result_file, output_video=None,
                       display='{time} [{frame}]', scale_bar=True,
                       border_buffer_cm=0, frame_compression=1,
                       time_duration=None, progress=True):
    """ function that crops a video to an antfarm.
    
    `result_file` is the file where the results from the video analysis are
        stored. This is usually a *.yaml file
    `output_video` denotes the filename where the result video should be
        written to.
    `display` determines what information is displayed. There are several
        variables that would be replaced by data:
            {time} the current time stamp
            {frame} the current frame number
    `scale_bar` determines whether a scale bar is shown
    `border_buffer_cm` sets the extra space (in units of cm) around the cropping
        rectangle that is included in the analysis
    `frame_compression` sets the compression factor that determines how many
        frames are dropped compared to the original video
    `time_duration` sets the maximal number of seconds the video is supposed to
        last. Additional frames will not be written.
    `progress` flag that determines whether the progress is displayed
    """
    logging.info('Analyze video `%s`', result_file)
    
    # load the respective result file 
    analyzer = load_result_file(result_file)

    # load the full original video
    video_info = analyzer.load_video(frames=(0, None))
    
    # crop the video to the cage
    video_input = analyzer.video
    cropping_cage = analyzer.data['pass1/video/cropping_cage']
    
    border_buffer_px = int(border_buffer_cm / analyzer.length_scale_mag)
    
    # change rectangle size if necessary
    if border_buffer_px != 0:
        cropping_rect = Rectangle.from_list(cropping_cage)
        video_rect = Rectangle(0, 0, video_input.width - 1,
                               video_input.height - 1)
        
        cropping_rect.buffer(border_buffer_px)
        cropping_rect.intersect(video_rect)
        cropping_cage = cropping_rect.to_list()
    
    if cropping_cage:
        # size_alignment=2 makes sure that the width and height are even numbers 
        video_input = FilterCrop(video_input, rect=cropping_cage,
                                 size_alignment=2)
        
    if frame_compression is not None and frame_compression != 1:
        video_input = FilterDropFrames(video_input,
                                       compression=frame_compression)
        
    if time_duration is not None:
        index_max = int(time_duration * video_input.fps)
        video_input = video_input[:index_max]
    
    # determine the filename of the output video
    if output_video is None:
        # determine the complete filename automatically
        movie_ext = analyzer.params['output/video/extension']
        filename = 'cropped'
        output_video = analyzer.get_filename(filename + movie_ext, 'video')
        
    elif '.' not in output_video:
        # determine the extension automatically
        movie_ext = analyzer.params['output/video/extension']
        output_video = output_video + movie_ext
    
    logging.info('Write output to `%s`', output_video)
    
    # create the video writer
    video_codec = analyzer.params['output/video/codec']
    video_bitrate = analyzer.params['output/video/crop_bitrate']
    if video_bitrate is None:
        video_bitrate = analyzer.params['output/video/bitrate']
    fps = video_input.fps
    video_output = VideoComposer(
        output_video, size=video_input.size, fps=fps,
        is_color=video_input.is_color, codec=video_codec, bitrate=video_bitrate,
    )
    
    # time label position
    label_pos = video_input.width // 2, 30

    # calculate size of scale bar and the position of its label
    pixel_size_cm = analyzer.data['pass2/pixel_size_cm']
    scale_bar_size_cm = 10
    scale_bar_size_px = np.round(scale_bar_size_cm / pixel_size_cm)
    scale_bar_rect = Rectangle(30, 50, scale_bar_size_px, 5)
    scale_bar_pos = (30 + scale_bar_size_px//2, 30)
    
    if progress:
        video_input = display_progress(video_input)
    
    for frame_id, frame in enumerate(video_input):
        video_output.set_frame(frame, copy=True)

        if scale_bar:
            # show a scale bar
            video_output.add_rectangle(scale_bar_rect, width=-1)
            video_output.add_text(str('%g cm' % scale_bar_size_cm),
                                  scale_bar_pos, color='w',
                                  anchor='upper center')

        # gather data about this frame
        frame_data = {'frame': frame_id}
        
        # calculate time stamp
        time_secs, time_frac = divmod(frame_id, fps)
        time_msecs = int(1000 * time_frac / fps)
        dt = datetime.timedelta(seconds=time_secs,
                                milliseconds=time_msecs)
        frame_data['time'] = str(dt)
            
        # output the display data
        if display:
            display_text = display.format(**frame_data)
            video_output.add_text(display_text, label_pos, color='w',
                                  anchor='upper center')

    # show summary
    frames_total = video_info['frames'][1] - video_info['frames'][0]
    frames_written = video_output.frames_written
    logging.info('%d (%d%%) of %d frames written', frames_written,
                 100 * frames_written // frames_total, frames_total)
    
    # close and finalize video
    try:
        video_output.close()
    except IOError:
        logging.exception('Error while writing out the debug video `%s`',
                          video_output)
        
Beispiel #2
0
    def produce_video(self):
        """ prepares everything for the debug output """
        # load parameters for video output        
        video_extension = self.params['output/video/extension']
        video_codec = self.params['output/video/codec']
        video_bitrate = self.params['output/video/bitrate']
        
        if self.video is None:
            self.load_video()
        
        filename = self.get_filename('video' + video_extension, 'video')
        video = VideoComposer(filename, size=self.video.size,
                              fps=self.video.fps, is_color=True,
                              output_period=self.params['output/output_period'],
                              codec=video_codec, bitrate=video_bitrate)
        
        mouse_track = self.data['pass2/mouse_trajectory']
        ground_profile = self.data['pass2/ground_profile']
        
        self.logger.info('Pass 2 - Start producing final video with %d frames', len(self.video))
        
        # we used the first frame to determine the cage dimensions in the first pass
        source_video = self.video[1:]
        
        track_first = 0
        tracks = self.data['pass1/objects/tracks']
        tracks.sort(key=lambda track: track.start)
        burrow_tracks = self.data['pass1/burrows/tracks']
        
        for frame_id, frame in enumerate(display_progress(source_video)):
            # set real video as background
            video.set_frame(frame)
        
            # plot the ground profile
            ground_line = ground_profile.get_groundline(frame_id)
            video.add_line(ground_line, is_closed=False,
                              mark_points=False, color='y')

            # indicate burrow centerline
            for burrow in burrow_tracks.find_burrows(frame_id):
                ground = GroundProfile(ground_line)
                video.add_line(burrow.get_centerline(ground),
                                  'k', is_closed=False, width=2)
        
            # indicate all moving objects
            # find the first track which is still active
            while tracks[track_first].end < frame_id:
                track_first += 1
            # draw all tracks that are still active
            for track in tracks[track_first:]:
                if track.start > frame_id:
                    # this is the last track that can possibly be active
                    break
                elif track.start <= frame_id <= track.end:
                    video.add_circle(track.get_pos(frame_id),
                                     self.params['mouse/model_radius'],
                                     '0.5', thickness=1)

            # indicate the mouse position
            if np.all(np.isfinite(mouse_track.pos[frame_id])):
                video.add_circle(mouse_track.pos[frame_id],
                                 self.params['mouse/model_radius'],
                                 'w', thickness=2)
                    
            
#                 # add additional debug information
            
            video.add_text(str(frame_id), (20, 20), anchor='top')   
#                 video.add_text(str(self.frame_id/self.fps), (20, 20), anchor='top')   
                
        video.close()
def make_underground_video(result_file, output_video=None,
                           display='{time} [{frame}]', scale_bar=True,
                           min_duration=60, blank_duration=5,
                           bouts_slice=slice(None, None), video_part=None):
    """ main routine of the program
    `result_file` is the file where the results from the video analysis are
        stored. This is usually a *.yaml file
    `output_video` denotes the filename where the result video should be
        written to.
    `display` determines what information is displayed. There are several
        variables that would be replaced by data:
            {time} the current time stamp
            {frame} the current frame number
    `scale_bar` determines whether a scale bar is shown
    `min_duration` determines how many frames the mouse has to be below ground
        for the bout to be included in the video
    `blank_duration` determines who many white frames are displayed between
        time intervals where the mouse is underground
    `bouts_slice` is a slice object that determines which bouts are included in
        the video.
    `video_part` determines which part of a longer video will be produced
    """
    logging.info('Analyze video `%s`', result_file)
    
    # load the respective result file 
    analyzer = load_result_file(result_file)
    
    # determine the bouts of this video
    bouts = get_underground_bouts(analyzer, bouts_slice, video_part)
    
    if len(bouts) == 0:
        raise RuntimeError('There are no bouts that could be turned into a '
                           'video. This could be a problem with finding the '
                           'mouse trajectory in the analysis or it could '
                           'indicate that the requested bouts_slice or '
                           'video_part resulted in empty data.')

    # load the original video
    video_info = analyzer.load_video()
    frame_offset = video_info['frames'][0]
    
    # crop the video to the cage
    video_input = analyzer.video
    cropping_cage = analyzer.data['pass1/video/cropping_cage']
    if cropping_cage:
        video_input = FilterCrop(video_input, rect=cropping_cage)
    
    # determine the filename of the output video
    if output_video is None:
        # determine the complete filename automatically
        movie_ext = analyzer.params['output/video/extension']
        if video_part is None:
            filename = 'underground'
        else:
            filename = 'underground_%d' % video_part
        output_video = analyzer.get_filename(filename + movie_ext,
                                             'video_underground')
        
    elif '.' not in output_video:
        # determine the extension automatically
        movie_ext = analyzer.params['output/video/extension']
        output_video = output_video + movie_ext
    
    logging.info('Write output to `%s`', output_video)
    
    # create the video writer
    video_codec = analyzer.params['output/video/codec']
    video_bitrate = analyzer.params['output/video/bitrate']
    fps = video_input.fps
    video_output = VideoComposer(
        output_video, size=video_input.size, fps=fps, is_color=False,
        codec=video_codec, bitrate=video_bitrate,
    )
    
    # create blank frame with mean color of video
    blank_frame = np.full(video_input.shape[1:], video_input[0].mean(),
                          dtype=np.uint8)
    
    # time label position
    label_pos = video_input.width // 2, 30

    # calculate size of scale bar and the position of its label
    pixel_size_cm = analyzer.data['pass2/pixel_size_cm']
    scale_bar_size_cm = 10
    scale_bar_size_px = np.round(scale_bar_size_cm / pixel_size_cm)
    scale_bar_rect = Rectangle(30, 50, scale_bar_size_px, 5)
    scale_bar_pos = (30 + scale_bar_size_px//2, 30)
    
    # iterate over all bouts
    for start, finish in display_progress(bouts):
        
        duration = finish - start + 1 
        
        # check whether bout is long enough
        if duration < min_duration:
            continue
        
        if video_output.frames_written > 1:
            # write blank frame
            for _ in xrange(blank_duration):
                video_output.set_frame(blank_frame)
                
        video_bout = video_input[start - frame_offset : 
                                 finish - frame_offset + 1]
        
        for frame_id, frame in enumerate(video_bout, start):
            video_output.set_frame(frame, copy=True)

            if scale_bar:
                # show a scale bar
                video_output.add_rectangle(scale_bar_rect, width=-1)
                video_output.add_text(str('%g cm' % scale_bar_size_cm),
                                      scale_bar_pos, color='w',
                                      anchor='upper center')

            # gather data about this frame
            frame_data = {'frame': frame_id}
            
            # calculate time stamp
            time_secs, time_frac = divmod(frame_id, fps)
            time_msecs = int(1000 * time_frac / fps)
            dt = datetime.timedelta(seconds=time_secs,
                                    milliseconds=time_msecs)
            frame_data['time'] = str(dt)
                
            # output the display data
            if display:
                display_text = display.format(**frame_data)
                video_output.add_text(display_text, label_pos, color='w',
                                      anchor='upper center')

    # show summary
    frames_total = video_info['frames'][1] - video_info['frames'][0]
    frames_written = video_output.frames_written
    logging.info('%d (%d%%) of %d frames written', frames_written,
                 100 * frames_written // frames_total, frames_total)
    
    # close and finalize video
    try:
        video_output.close()
    except IOError:
        logging.exception('Error while writing out the debug video `%s`',
                          video_output)