<commit_before>'''
Created on Aug 5, 2014

@author: zwicker

Note that the OpenCV convention is to store images in [row, column] format
Thus, a point in an image is referred to as image[coord_y, coord_x]
However, a single point is stored as point = (coord_x, coord_y)
Similarly, we store rectangles as (coord_x, coord_y, width, height)

Generally, x-values increase from left to right, while y-values increase from
top to bottom. The origin is thus in the upper left corner.
'''

from __future__ import division

import os
import logging
import datetime

import numpy as np
import scipy.ndimage as ndimage
from scipy.optimize import leastsq
import cv2
import yaml
import h5py

from video.io import VideoFileStack, VideoFileWriter, ImageShow
from video.filters import FilterBlur, FilterCrop, FilterMonochrome
from video.analysis.regions import (get_largest_region, find_bounding_box,
                                    rect_to_slices, corners_to_rect)
from video.analysis.curves import (curve_length, make_curve_equidistant,
                                   simplify_curve, point_distance)
from video.utils import (display_progress, ensure_directory,
                         prepare_data_for_yaml, homogenize_arraylist)
from video.composer import VideoComposerListener, VideoComposer

import debug


TRACKING_PARAMETERS_DEFAULT = {
    # number of initial frames to not analyze
    'video.ignore_initial_frames': 0,
    # radius of the blur filter
    'video.blur_radius': 3,
    
    # thresholds for cage dimension
    'cage.minimal_width': 650,
    'cage.minimal_height': 400,
                               
    # determines the rate with which the background is adapted
    'background.adaptation_rate': 0.01,
    
    # spacing of the points in the sand profile
    'sand_profile.point_spacing': 20,
    # adapt the sand profile only every number of frames
    'sand_profile.adaptation_interval': 100,
    # width of the ridge in pixel
    'sand_profile.width': 5,
        
    # `mouse.intensity_threshold` determines how much brighter than the
    # background (usually the sky) has the mouse to be. This value is
    # measured in terms of standard deviations of the sky color
    'mouse.intensity_threshold': 1.2, #1.5,
    # radius of the mouse model in pixel
    'mouse.size': 25,
    # maximal speed of the mouse in pixel per frame
    'mouse.max_speed': 30, 
    # after how many frames do we think we have lost the mouse entirely
    'mouse.lost_threshold': 100,
}


class MouseMovie(object):
    """
    analyzes mouse movies
    """
    
    video_filename_pattern = 'raw_video/*'
    video_output_codec = 'libx264'
    video_output_extension = '.mov'
    video_output_bitrate = None
    
    def __init__(self, folder, frames=None, crop=None, prefix='',
                 tracking_parameters=None, debug_output=None):
        """ initializes the whole mouse tracking and prepares the video filters """
        
        # initialize the dictionary holding result information
        self.result = {}
        self.log_event('init_begin', 'Start initializing the video analysis.')
        self.folder = folder
        
        # initialize video
        self.video = VideoFileStack(os.path.join(folder, self.video_filename_pattern))
        self.result['video_raw'] = {'filename_pattern': self.video_filename_pattern,
                                    'frame_count': self.video.frame_count,
                                    'size': '%d x %d' % self.video.size,
                                    'fps': self.video.fps,}
        
        # restrict the analysis to an interval of frames
        if frames is not None:
            self.video = self.video[frames[0]:frames[1]]
        else:
            frames = (0, self.video.frame_count) 
        
        self.prefix = prefix + '_' if prefix else ''
        self.debug_output = [] if debug_output is None else debug_output
        self.params = TRACKING_PARAMETERS_DEFAULT.copy()
        if tracking_parameters is not None:
            self.params.update(tracking_parameters)
        self.result['tracking_parameters'] = self.params
        
        # setup internal structures that will be filled by analyzing the video
        self._cache = {}           # cache that some functions might want to use
        self.debug = {}            # dictionary holding debug information
        self.sand_profile = None   # current model of the sand profile
        self.mouse_pos = (np.nan, np.nan)
        self._mouse_not_seen = 0   # number of previous frames in which the mouse has not been seen 
        self._explored_area = None # region the mouse has explored yet
        self.result['mouse.has_moved'] = False

        # restrict the video to the region of interest (the cage)
        self.crop_video_to_cage(crop)
        self.result['video_analyzed'] = {'frame_slice': '%d to %d' % frames,
                                         'frame_count': self.video.frame_count,
                                         'size': '%d x %d' % self.video.size,
                                         'fps': self.video.fps,}

        # blur the video to reduce noise effects    
        self.video_blurred = FilterBlur(self.video, self.params['video.blur_radius'])
        first_frame = self.video_blurred[0]
        
        # initialize the background model
        self._background = np.array(first_frame, dtype=float)

        # estimate colors of sand and sky
        self.find_color_estimates(first_frame)
        
        # estimate initial sand profile
        self.find_sand_profile(first_frame)

        self.log_event('init_end', 'Finished initializing the video analysis.')


    def process_video(self):
        """ processes the entire video """

        self.log_event('run_begin', 'Start iterating through the frames.')
        
        self.debug_setup()
        self.process_setup()
        self.result['sand_profile'] = []

        try:
            # iterate over the video and analyze it
            for frame_id, frame in enumerate(display_progress(self.video_blurred)):
                
                if frame_id < self.params['video.ignore_initial_frames']:
                    # ignore early frames and only adapt background<commit_msg>Added that colors are checked every couple number of frames<commit_after>'''
Created on Aug 5, 2014

@author: zwicker

Note that the OpenCV convention is to store images in [row, column] format
Thus, a point in an image is referred to as image[coord_y, coord_x]
However, a single point is stored as point = (coord_x, coord_y)
Similarly, we store rectangles as (coord_x, coord_y, width, height)

Generally, x-values increase from left to right, while y-values increase from
top to bottom. The origin is thus in the upper left corner.
'''

from __future__ import division

import os
import logging
import datetime

import numpy as np
import scipy.ndimage as ndimage
from scipy.optimize import leastsq
import cv2
import yaml
import h5py

from video.io import VideoFileStack, VideoFileWriter, ImageShow
from video.filters import FilterBlur, FilterCrop, FilterMonochrome
from video.analysis.regions import (get_largest_region, find_bounding_box,
                                    rect_to_slices, corners_to_rect)
from video.analysis.curves import (curve_length, make_curve_equidistant,
                                   simplify_curve, point_distance)
from video.utils import (display_progress, ensure_directory,
                         prepare_data_for_yaml, homogenize_arraylist)
from video.composer import VideoComposerListener, VideoComposer

import debug


TRACKING_PARAMETERS_DEFAULT = {
    # number of initial frames to not analyze
    'video.ignore_initial_frames': 0,
    # radius of the blur filter
    'video.blur_radius': 3,
    
    # thresholds for cage dimension
    'cage.minimal_width': 650,
    'cage.minimal_height': 400,
                               
    # determine the colors every ? frames
    'colors.adaptation_interval': 1000,
                               
    # determines the rate with which the background is adapted
    'background.adaptation_rate': 0.01,
    
    # spacing of the points in the sand profile
    'sand_profile.point_spacing': 20,
    # adapt the sand profile only every number of frames
    'sand_profile.adaptation_interval': 100,
    # width of the ridge in pixel
    'sand_profile.width': 5,
        
    # `mouse.intensity_threshold` determines how much brighter than the
    # background (usually the sky) has the mouse to be. This value is
    # measured in terms of standard deviations of the sky color
    'mouse.intensity_threshold': 1.2, #1.5,
    # radius of the mouse model in pixel
    'mouse.size': 25,
    # maximal speed of the mouse in pixel per frame
    'mouse.max_speed': 30, 
    # after how many frames do we think we have lost the mouse entirely
    'mouse.lost_threshold': 100,
}


class MouseMovie(object):
    """
    analyzes mouse movies
    """
    
    video_filename_pattern = 'raw_video/*'
    video_output_codec = 'libx264'
    video_output_extension = '.mov'
    video_output_bitrate = None
    
    def __init__(self, folder, frames=None, crop=None, prefix='',
                 tracking_parameters=None, debug_output=None):
        """ initializes the whole mouse tracking and prepares the video filters """
        
        # initialize the dictionary holding result information
        self.result = {}
        self.log_event('init_begin', 'Start initializing the video analysis.')
        self.folder = folder
        
        # initialize video
        self.video = VideoFileStack(os.path.join(folder, self.video_filename_pattern))
        self.result['video_raw'] = {'filename_pattern': self.video_filename_pattern,
                                    'frame_count': self.video.frame_count,
                                    'size': '%d x %d' % self.video.size,
                                    'fps': self.video.fps,}
        
        # restrict the analysis to an interval of frames
        if frames is not None:
            self.video = self.video[frames[0]:frames[1]]
        else:
            frames = (0, self.video.frame_count) 
        
        self.prefix = prefix + '_' if prefix else ''
        self.debug_output = [] if debug_output is None else debug_output
        self.params = TRACKING_PARAMETERS_DEFAULT.copy()
        if tracking_parameters is not None:
            self.params.update(tracking_parameters)
        self.result['tracking_parameters'] = self.params
        
        # setup internal structures that will be filled by analyzing the video
        self._cache = {}           # cache that some functions might want to use
        self.debug = {}            # dictionary holding debug information
        self.sand_profile = None   # current model of the sand profile
        self.mouse_pos = (np.nan, np.nan)
        self._mouse_not_seen = 0   # number of previous frames in which the mouse has not been seen 
        self._explored_area = None # region the mouse has explored yet
        self.result['mouse.has_moved'] = False

        # restrict the video to the region of interest (the cage)
        self.crop_video_to_cage(crop)
        self.result['video_analyzed'] = {'frame_slice': '%d to %d' % frames,
                                         'frame_count': self.video.frame_count,
                                         'size': '%d x %d' % self.video.size,
                                         'fps': self.video.fps,}

        # blur the video to reduce noise effects    
        self.video_blurred = FilterBlur(self.video, self.params['video.blur_radius'])
        first_frame = self.video_blurred[0]
        
        # initialize the background model
        self._background = np.array(first_frame, dtype=float)

        # estimate colors of sand and sky
        self.find_color_estimates(first_frame)
        
        # estimate initial sand profile
        self.find_sand_profile(first_frame)

        self.log_event('init_end', 'Finished initializing the video analysis.')


    def process_video(self):
        """ processes the entire video """

        self.log_event('run_begin', 'Start iterating through the frames.')
        
        self.debug_setup()
        self.process_setup()
        self.result['sand_profile'] = []

        try:
            # iterate over the video and analyze it
            for frame_id, frame in enumerate(display_progress(self.video_blurred)):
                
                if frame_id % self.params['colors.adaptation_interval'] == 0:
                    self.find_color_estimates(frame)
                
                if frame_id < self.params['video.ignore_initial_frames']:
                    # ignore early frames and only adapt background
{'fraction_non_alphanumeric': 0.07668182530970676, 'fraction_numerical': 0.007135016465422613, 'mean_word_length': 2.3592309718198576, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4894323', 'n_tokens_mistral': 3537, 'n_tokens_neox': 3327, 'n_words': 1100}