Exemplo n.º 1
0
def quantify_torsion(WINDOW_RADIUS,
                     RESOLUTION,
                     torsion_mode,
                     transform_mode,
                     video,
                     start_frame,
                     reference_frame,
                     end_frame,
                     pupil_list,
                     blink_list,
                     threshold,
                     alternate,
                     WINDOW_THETA=None,
                     SEGMENT_THETA=None,
                     upper_iris=None,
                     lower_iris=None,
                     feature_coords=None):
    '''
    Utilizes the 2D cross correlation algorithm xcorr2d to measure and return torsion using the settings given.

    Inputs:
        WINDOW_RADIUS:
            Integer
            Mandatory input which sets the radial thickness of the iris transform.

        RESOLUTION:
            Double
            Mandatory input with sets the upsampling factor or the interpolation resolution depending on settings below.

        torsion_mode:
            String
            Mandatory input which determines whether interpolation or upsampling is used.
            if torsion_mode = 'interp', then interpolation is used and RESOLUTION is assumed to be interpolation resolution. Consequently an upsampling factor of 1 is used in the transform.
            if torsion_mode = 'upsample', then upsampling is used and RESOLUTION is assumed to be the upsampling factor.

        transform_mode:
            String
            Mandatory input which determines whether a subset of the iris is used or the full iris is used during correlation.
            if transform_mode = 'subset', a subset of the iris is used.
            if transform_mode = 'full', the full iris us used.

        video:
            Video object
            Mandatory input

        start_frame:
            Integer
            Mandatory input which is the index of the reference frame.

        end_frame:
            Integer
            Mandatory input which is the index of the last frame to analyze.

        pupil_list:
            dictionary of pupil objects
            key: (int) video frame
            value: pupil object

        blink_list:
            dictionary of whether or not a frame captures a blink
            key: (int) video frame
            value: 1 - blink
                   0 - no blink
                   None - None

        WINDOW_THETA:
            Integer
            Angle bounds above/below the feature that define the portion of the iris that is to be included in the reference iris window. This window should be smaller than the segment.
            Mandatory input if transform_mode = 'subset'.

        SEGMENT_THETA:
            Integer
            Angle bounds above/below the feature that define the portion of the iris that is to be included in each segment, for which the window is to be located in.
            Mandatory input if transform_mode = 'subset'.

        upper_iris_occ:
            dictionary, {'c': column index, 'r': row index}
            Holds the [row,column] coordinates of the upper boundary of the iris that is not occluded by eyelids or eyelashes.

        lower_iris_occ:
            dictionary, {'c': column index, 'r': row index}
            Holds the [row,column] coordinates of the lower boundary of the iris that is not occluded by eyelids or eyelashes.

        feature_coords:
            dictionary, {'c': column index, 'r': row index}
            Holds the dictionary of feature coordinates tracked during subset correlation.
            Mandatory input if transform_mode = 'subset'.

    Returns:

        torsion:
            Dictionary
            key = frame number
            value = rotation from reference frame
        torsion_deriative:
            Dictionary
            key = frame number
            value = rotation from previous frame
    '''

    upsample_factor = 1
    noise_replace = False

    if torsion_mode == 'interp':
        pass
    elif torsion_mode == 'upsample':
        upsample_factor = RESOLUTION

    if transform_mode == 'full':
        if upper_iris and lower_iris:
            noise_replace = True
        start = 0
        reference_bounds = (0, 360)  # what are these? 360 degrees?
        comparison_bounds = (0, 360)

    elif transform_mode == 'subset':
        feature_r, feature_theta = iris.get_polar_coord(
            feature_coords['r'], feature_coords['c'], pupil_list[start_frame])
        reference_bounds = (feature_theta - WINDOW_THETA,
                            feature_theta + WINDOW_THETA)
        comparison_bounds = (feature_theta - SEGMENT_THETA,
                             feature_theta + SEGMENT_THETA)
        start = int((SEGMENT_THETA - WINDOW_THETA) / upsample_factor)

    # TODO: This is actually such a dumb way, copying and pasting code.
    elif transform_mode == 'alternate':
        # Get the aspects as if you were doing a full iris analysis
        if upper_iris and lower_iris:
            noise_replace = True
        start = 0
        reference_bounds = (0, 360)  # what are these? 360 degrees?
        comparison_bounds = (0, 360)

        # Get the aspects as if you are doing the subset
        feature_r, feature_theta = iris.get_polar_coord(
            feature_coords['r'], feature_coords['c'], pupil_list[start_frame])
        reference_bounds_sr = (feature_theta - WINDOW_THETA,
                               feature_theta + WINDOW_THETA)
        comparison_bounds_sr = (feature_theta - SEGMENT_THETA,
                                feature_theta + SEGMENT_THETA)
        start_sr = int((SEGMENT_THETA - WINDOW_THETA) / upsample_factor)

    # get the reference window from the first frame of the video
    # this will be the base for all torsion ie. all rotation is relative to this window
    if start_frame == reference_frame:
        if alternate:
            first_window_sr = iris.iris_transform(
                video[start_frame],
                pupil_list[start_frame],
                WINDOW_RADIUS,
                theta_resolution=upsample_factor,
                theta_window=reference_bounds_sr)
        first_window = iris.iris_transform(video[start_frame],
                                           pupil_list[start_frame],
                                           WINDOW_RADIUS,
                                           theta_resolution=upsample_factor,
                                           theta_window=reference_bounds)
    else:
        ref_pupil = pupil.Pupil(video[reference_frame], threshold)
        if alternate:
            first_window_sr = iris.iris_transform(
                video[reference_frame],
                ref_pupil,
                WINDOW_RADIUS,
                theta_resolution=upsample_factor,
                theta_window=reference_bounds_sr)
        first_window = iris.iris_transform(video[reference_frame],
                                           ref_pupil,
                                           WINDOW_RADIUS,
                                           theta_resolution=upsample_factor,
                                           theta_window=reference_bounds)

    # TODO: If noise replace is selected, cannot select segment removal
    if noise_replace:

        # transform (column,row) into (theta,r) space about pupil centre
        # get the boundaries of usable iris in polar
        upper_iris_r, upper_iris_theta = iris.get_polar_coord(
            upper_iris['r'], upper_iris['c'], pupil_list[start_frame])
        lower_iris_r, lower_iris_theta = iris.get_polar_coord(
            lower_iris['r'], lower_iris['c'], pupil_list[start_frame])

        # mirrors the upper angular boundary across the vertical axis
        upper_occlusion_theta = (90 - np.absolute(upper_iris_theta - 90),
                                 90 + np.absolute(upper_iris_theta - 90))

        # mirrors the lower angular boundary across the vertical axis
        # deal with the branch cut at 270
        if lower_iris_theta < 0:
            lower_occlusion_theta = (-90 - np.absolute(lower_iris_theta + 90),
                                     -90 + np.absolute(lower_iris_theta + 90))
        else:
            lower_occlusion_theta = (-90 - np.absolute(lower_iris_theta - 270),
                                     -90 + np.absolute(lower_iris_theta - 270))

        # replace occluded sections with noise
        first_window = eyelid_removal.noise_replace(first_window,
                                                    upper_occlusion_theta,
                                                    lower_occlusion_theta)

    # Nani is this? Why is it not with the other stuff above?
    if transform_mode == 'full' or transform_mode == 'alternate':
        # extend iris window
        first_window = eyelid_removal.iris_extension(
            first_window,
            theta_resolution=upsample_factor,
            lower_theta=-pre.MAX_ANGLE,
            upper_theta=pre.MAX_ANGLE)

    # TODO: Add a button to show iris segments

    torsion = {}
    torsion_derivative = {}
    # find torsion between start_frame+1:last_frame
    for i, frame in tqdm(enumerate(video[start_frame:end_frame])):
        frame_loc = i + start_frame
        # check if a pupil exists, or if there is a blink
        if not pupil_list[frame_loc] or blink_list[frame_loc] is None:
            # if there is no pupil, torsion cannot be calculated
            torsion[frame_loc] = None
            torsion_derivative[frame_loc] = None
            print(
                'WARNING: No pupil in frame: %d \n Torsion cannot be calculated'
                % (frame_loc))
        else:
            # unwrap the iris (convert into polar)
            #current_frame = iris.iris_transform(frame, pupil_list[frame_loc], WINDOW_RADIUS, theta_resolution = upsample_factor, theta_window = comparison_bounds)

            # TODO: add when eyelid is not found???
            if alternate and blink_list[frame_loc] == 1:
                # TODO: Need to add detection, this method of checking if 0 exists is too much of a gong show
                current_frame = iris.iris_transform(
                    frame,
                    pupil_list[frame_loc],
                    WINDOW_RADIUS,
                    theta_resolution=upsample_factor,
                    theta_window=comparison_bounds_sr)
                # get the degree of rotation of the current frame based on reference frame
                deg = xcorr2d.xcorr2d(current_frame,
                                      first_window_sr,
                                      start=start_sr,
                                      prev_deg=None,
                                      torsion_mode=torsion_mode,
                                      resolution=RESOLUTION,
                                      threshold=0,
                                      max_angle=pre.MAX_ANGLE)
                if i > 0:
                    # Calculate torsion based off of previous window
                    previous_window_sr = iris.iris_transform(
                        video[frame_loc - 1],
                        pupil_list[frame_loc - 1],
                        WINDOW_RADIUS,
                        theta_resolution=upsample_factor,
                        theta_window=reference_bounds_sr)
                    # get the degree of rotation of the current frame based on previous frame
                    previous_deg = xcorr2d.xcorr2d(current_frame,
                                                   previous_window_sr,
                                                   start=start_sr,
                                                   prev_deg=None,
                                                   torsion_mode=torsion_mode,
                                                   resolution=RESOLUTION,
                                                   threshold=0,
                                                   max_angle=pre.MAX_ANGLE)
                else:
                    previous_deg = None
            else:
                current_frame = iris.iris_transform(
                    frame,
                    pupil_list[frame_loc],
                    WINDOW_RADIUS,
                    theta_resolution=upsample_factor,
                    theta_window=comparison_bounds)
                # get the degree of rotation of the current frame based on reference frame
                deg = xcorr2d.xcorr2d(current_frame,
                                      first_window,
                                      start=start,
                                      prev_deg=None,
                                      torsion_mode=torsion_mode,
                                      resolution=RESOLUTION,
                                      threshold=0,
                                      max_angle=pre.MAX_ANGLE)
                if i > 0:
                    # get the previous frame
                    previous_window = iris.iris_transform(
                        video[frame_loc - 1],
                        pupil_list[frame_loc - 1],
                        WINDOW_RADIUS,
                        theta_resolution=upsample_factor,
                        theta_window=reference_bounds)
                    previous_window = eyelid_removal.iris_extension(
                        previous_window,
                        theta_resolution=upsample_factor,
                        lower_theta=-pre.MAX_ANGLE,
                        upper_theta=pre.MAX_ANGLE)

                    # get the degree of rotation of the current frame based on previous frame
                    previous_deg = xcorr2d.xcorr2d(current_frame,
                                                   previous_window,
                                                   start=start,
                                                   prev_deg=None,
                                                   torsion_mode=torsion_mode,
                                                   resolution=RESOLUTION,
                                                   threshold=0,
                                                   max_angle=pre.MAX_ANGLE)
                else:
                    previous_deg = None
            # save the torsion3
            torsion[frame_loc] = deg
            torsion_derivative[frame_loc] = previous_deg
            '''
            # Get the change in angle compared to previous frame
            if frame_loc != start_frame :
                if deg is None or torsion[frame_loc-1] is None:
                    torsion_derivative[frame_loc] = None
                else:
                    torsion_derivative[frame_loc] = deg - torsion[frame_loc-1]
            else:
                torsion_derivative[frame_loc] = None
            '''

    return torsion, torsion_derivative
Exemplo n.º 2
0
def corr2d(video_path, verborose=True, **kw):

    # save all results to dictionary
    results = {
        'interp': {
            'full': [],
            'subset': []
        },
        'upsample': {
            'full': [],
            'subset': []
        },
        'standard': {
            'full': [],
            'subset': []
        }
    }

    # get parameters from kw args
    start_frame = kw.get('start_frame', 0)
    end_frame = kw.get('end_frame', -1)
    transform_resolution = kw.get('transform_resolution', 1)
    interp_resolution = kw.get('interp_resolution', 0.1)
    upsample_resolution = kw.get('upsample_resolution', 0.1)
    interp_threshold = kw.get('interp_threshold', 0.3)
    upsample_threshold = kw.get('upsample_threshold', 0)
    interp_start = kw.get('interp_start', 250)
    upsample_start = kw.get('upsample_start', 2500)
    window_length = kw.get('window_length', 50)
    window_height = kw.get('window_height', 30)
    max_angle = kw.get('max_angle', 25)
    pupil_threshold = kw.get('pupil_threshold', 10)
    im_crop = kw.get('im_crop', None) # List of crop indeces of form [row_lower_lim, row_upper_lim, col_lower_lim, col_upper_lim]

    video_name = os.path.basename(video_path)

    # save all results as data objects within in a folder
    now = datetime.datetime.now().strftime("%Y_%m_%d")
    file_path = os.path.abspath(os.path.join(os.curdir, 'results', video_name, now))

    if not os.path.isdir(file_path):
        os.makedirs(file_path)

    # create video object
    video = v.Video(video_path)

    metadata = kw
    # TODO add more?
    metadata['VIDEO_FPS'] = video.fps

    # create the reference windows
    first_frame = video[start_frame]

    # Crop the video frame
    if im_crop is not None:
        first_frame = first_frame[im_crop[0]:im_crop[1], im_crop[2]:im_crop[3]]

    iris_segment_interp = iris_transform(first_frame, Pupil(first_frame, threshold=pupil_threshold), window_height, theta_resolution=transform_resolution)
    iris_segment_upsample = iris_transform(first_frame, Pupil(first_frame, threshold=pupil_threshold), window_height, theta_resolution=upsample_resolution)

    reference_windows = {
        'interp': {
            'subset': iris_segment_interp[:, slice(interp_start,interp_start+window_length)],
            'extend': extend(iris_segment_interp, diff=max_angle)
        },
        'upsample': {
            'subset': iris_segment_upsample[:, slice(upsample_start,upsample_start+int(window_length/upsample_resolution))],
            'extend': extend(iris_segment_upsample, diff=int(max_angle/upsample_resolution))
        }
    }

    if verborose:
        print('Starting batch 2D Cross Correlation with upsample and interpolation ...')
        start_time = time.time()

    for segment in video[start_frame+1:end_frame]:
        # Crop frame
        if im_crop is not None:
            segment = segment[im_crop[0]:im_crop[1], im_crop[2]:im_crop[3]]
        # segment = segment[0:500, 500:1100]

        interp_seg = iris_transform(segment, Pupil(segment, threshold=pupil_threshold), window_height, theta_resolution=transform_resolution)
        upsample_seg = iris_transform(segment, Pupil(segment, threshold=pupil_threshold), window_height, theta_resolution=upsample_resolution)

        # standard method is upsampling with resolution 1
        t_standard_full = xcorr2d(interp_seg, reference_windows['interp']['extend'], 0, resolution=1, threshold=0, torsion_mode='upsample')
        t_standard_subset = xcorr2d(interp_seg, reference_windows['interp']['subset'], interp_start, resolution=1, threshold=-1, torsion_mode='upsample')
        t_interp_full = xcorr2d(interp_seg, reference_windows['interp']['extend'], 0, resolution=interp_resolution, threshold=interp_threshold, torsion_mode='interp')
        t_interp_subset = xcorr2d(interp_seg, reference_windows['interp']['subset'], interp_start, resolution=interp_resolution, threshold=interp_threshold, torsion_mode='interp')
        t_upsample_full = xcorr2d(upsample_seg, reference_windows['upsample']['extend'], 0, resolution=interp_resolution, threshold=interp_threshold, torsion_mode='upsample')
        t_upsample_subset = xcorr2d(upsample_seg, reference_windows['upsample']['subset'], upsample_start, resolution=upsample_resolution, threshold=upsample_threshold, torsion_mode='upsample')

        results['standard']['full'].append(t_standard_full)
        results['standard']['subset'].append(t_standard_subset)
        results['interp']['full'].append(t_interp_full)
        results['interp']['subset'].append(t_interp_subset)
        results['upsample']['full'].append(t_upsample_full)
        results['upsample']['subset'].append(t_upsample_subset)

        if verborose:
            print('Elapsed Time: {}s'.format(round(time.time() - start_time,2)), sep=' ', end='\r', flush=True)

    if verborose:
        print('Duration:', time.time() - start_time)

    for method in results:
        for mode in results[method]:
            obj = Data('_'.join((method, mode)), file_path)
            obj.set(results[method][mode], start_frame, metadata)
            obj.save()

            if verborose:
                print('Saving {} {} results.'.format(method, mode))
Exemplo n.º 3
0
def quantify_torsion(WINDOW_RADIUS,
                     RESOLUTION,
                     torsion_mode,
                     transform_mode,
                     video,
                     start_frame,
                     reference_frame,
                     end_frame,
                     pupil_list,
                     threshold,
                     WINDOW_THETA=None,
                     SEGMENT_THETA=None,
                     upper_iris=None,
                     lower_iris=None,
                     feature_coords=None):
    '''
    Utilizes the 2D cross correlation algorithm xcorr2d to measure and return torsion using the settings given.

    Inputs:
        WINDOW_RADIUS:
            Integer
            Mandatory input which sets the radial thickness of the iris transform.

        RESOLUTION:
            Double
            Mandatory input with sets the upsampling factor or the interpolation resolution depending on settings below.

        torsion_mode:
            String
            Mandatory input which determines whether interpolation or upsampling is used.
            if torsion_mode = 'interp', then interpolation is used and RESOLUTION is assumed to be interpolation resolution. Consequently an upsampling factor of 1 is used in the transform.
            if torsion_mode = 'upsample', then upsampling is used and RESOLUTION is assumed to be the upsampling factor.

        transform_mode:
            String
            Mandatory input which determines whether a subset of the iris is used or the full iris is used during correlation.
            if transform_mode = 'subset', a subset of the iris is used.
            if transform_mode = 'full', the full iris us used.

        video:
            Video object
            Mandatory input

        start_frame:
            Integer
            Mandatory input which is the index of the reference frame.

        end_frame:
            Integer
            Mandatory input which is the index of the last frame to analyze.

        pupil_list:
            dictionary of pupil objects
            key: (int) video frame
            value: pupil object

        WINDOW_THETA:
            Integer
            Angle bounds above/below the feature that define the portion of the iris that is to be included in the reference iris window. This window should be smaller than the segment.
            Mandatory input if transform_mode = 'subset'.

        SEGMENT_THETA:
            Integer
            Angle bounds above/below the feature that define the portion of the iris that is to be included in each segment, for which the window is to be located in.
            Mandatory input if transform_mode = 'subset'.

        upper_iris_occ:
            dictionary, {'c': column index, 'r': row index}
            Holds the [row,column] coordinates of the upper boundary of the iris that is not occluded by eyelids or eyelashes.

        lower_iris_occ:
            dictionary, {'c': column index, 'r': row index}
            Holds the [row,column] coordinates of the lower boundary of the iris that is not occluded by eyelids or eyelashes.

        feature_coords:
            dictionary, {'c': column index, 'r': row index}
            Holds the dictionary of feature coordinates tracked during subset correlation.
            Mandatory input if transform_mode = 'subset'.

    Returns:

        torsion:
            Dictionary
            key = frame number
            value = rotation from reference frame
    '''

    upsample_factor = 1
    noise_replace = False

    if torsion_mode == 'interp':
        pass
    elif torsion_mode == 'upsample':
        upsample_factor = RESOLUTION

    if transform_mode == 'subset':
        feature_r, feature_theta = iris.get_polar_coord(
            feature_coords['r'], feature_coords['c'], pupil_list[start_frame])
        reference_bounds = (feature_theta - WINDOW_THETA,
                            feature_theta + WINDOW_THETA)
        comparison_bounds = (feature_theta - SEGMENT_THETA,
                             feature_theta + SEGMENT_THETA)
        start = int((SEGMENT_THETA - WINDOW_THETA) / upsample_factor)
    elif transform_mode == 'full':
        if upper_iris and lower_iris:
            noise_replace = True
        start = 0
        reference_bounds = (0, 360)
        comparison_bounds = (0, 360)

    # get the reference window from the first frame of the video
    # this will be the base for all torsion ie. all rotation is relative to this window
    if start_frame == reference_frame:
        first_window = iris.iris_transform(video[start_frame],
                                           pupil_list[start_frame],
                                           WINDOW_RADIUS,
                                           theta_resolution=upsample_factor,
                                           theta_window=reference_bounds)
    else:
        ref_pupil = pupil.Pupil(video[reference_frame], threshold)
        first_window = iris.iris_transform(video[reference_frame],
                                           ref_pupil,
                                           WINDOW_RADIUS,
                                           theta_resolution=upsample_factor,
                                           theta_window=reference_bounds)

    if noise_replace:

        # transform (colum,row) into (theta,r) space about pupil centre
        # get the boundaries of usable iris in polar
        upper_iris_r, upper_iris_theta = iris.get_polar_coord(
            upper_iris['r'], upper_iris['c'], pupil_list[start_frame])
        lower_iris_r, lower_iris_theta = iris.get_polar_coord(
            lower_iris['r'], lower_iris['c'], pupil_list[start_frame])

        # mirrors the upper angular boundary across the vertical axis
        upper_occlusion_theta = (90 - np.absolute(upper_iris_theta - 90),
                                 90 + np.absolute(upper_iris_theta - 90))

        # mirrors the lower angular boundary across the vertical axis
        # deal with the branch cut at 270
        if lower_iris_theta < 0:
            lower_occlusion_theta = (-90 - np.absolute(lower_iris_theta + 90),
                                     -90 + np.absolute(lower_iris_theta + 90))
        else:
            lower_occlusion_theta = (-90 - np.absolute(lower_iris_theta - 270),
                                     -90 + np.absolute(lower_iris_theta - 270))

        # replace occluded sections with noise
        first_window = eyelid_removal.noise_replace(first_window,
                                                    upper_occlusion_theta,
                                                    lower_occlusion_theta)

    if transform_mode == 'full':
        # extend iris window
        first_window = eyelid_removal.iris_extension(
            first_window,
            theta_resolution=upsample_factor,
            lower_theta=-pre.MAX_ANGLE,
            upper_theta=pre.MAX_ANGLE)

    torsion = {}
    # find torsion between start_frame+1:last_frame
    for i, frame in tqdm(enumerate(video[start_frame:end_frame])):
        frame_loc = i + start_frame
        # check if a pupil exists
        if not pupil_list[frame_loc]:
            # if there is no pupil, torsion cannot be calculated
            torsion[frame_loc] = None
            print(
                'WARNING: No pupil in frame: %d \n Torsion cannot be calculated'
                % (frame_loc))
        else:
            # unwrap the iris (convert into polar)
            current_frame = iris.iris_transform(
                frame,
                pupil_list[frame_loc],
                WINDOW_RADIUS,
                theta_resolution=upsample_factor,
                theta_window=comparison_bounds)
            # get the degree of rotation of the current frame
            deg = xcorr2d.xcorr2d(current_frame,
                                  first_window,
                                  start=start,
                                  prev_deg=None,
                                  torsion_mode=torsion_mode,
                                  resolution=RESOLUTION,
                                  threshold=0,
                                  max_angle=pre.MAX_ANGLE)
            # save the torsion
            torsion[frame_loc] = deg

    return torsion
Exemplo n.º 4
0
def interpolation_subset_method(video_path, verborose=True, **kw):

    # get parameters from kw args
    start_frame = kw.get('start_frame', 0)
    end_frame = kw.get('end_frame', -1)
    transform_resolution = kw.get('transform_resolution', 1)
    interp_resolution = kw.get('interp_resolution', 0.1)
    upsample_resolution = kw.get('upsample_resolution', 0.1)
    interp_threshold = kw.get('interp_threshold', 0.3)
    upsample_threshold = kw.get('upsample_threshold', 0)
    interp_start = kw.get('interp_start', 250)
    upsample_start = kw.get('upsample_start', 2500)
    window_length = kw.get('window_length', 50)
    window_height = kw.get('window_height', 30)
    max_angle = kw.get('max_angle', 25)
    pupil_threshold = kw.get('pupil_threshold', 10)
    im_crop = kw.get('im_crop', None) # List of crop indeces of form [row_lower_lim, row_upper_lim, col_lower_lim, col_upper_lim]

    video_name = os.path.basename(video_path)

    # Create dict to temporarily hold important data
    data = {
        'pupil_list' : [],
        'torsion' : [],
    }

    # save all results as data objects within in a folder
    now = datetime.datetime.now().strftime("%Y_%m_%d")
    file_path = os.path.abspath(os.path.join(os.curdir, 'results', video_name, now))
    if not os.path.isdir(file_path):
        os.makedirs(file_path)

    # create video object
    video = v.Video(video_path)

    metadata = kw
    # TODO add more?
    metadata['VIDEO_FPS'] = video.fps

    # create the reference windows
    first_frame = video[start_frame]

    # Crop the video frame
    if im_crop is not None:
        first_frame = first_frame[im_crop[0]:im_crop[1], im_crop[2]:im_crop[3]]

    # Find first frame pupil and populate data lists
    pup = Pupil(first_frame, threshold=pupil_threshold)
    data['pupil_list'].append(pup)
    data['torsion'].append(0)

    # Create the reference window from the first frame
    iris_segment_interp = iris_transform(first_frame, pup, window_height, theta_resolution=transform_resolution)
    reference_window = iris_segment_interp[:, slice(interp_start,interp_start+window_length)]

    if verborose:
        print('Starting batch 2D Cross Correlation with upsample and interpolation ...')
        start_time = time.time()

    for I in video[start_frame+1:end_frame]:
        # Crop frame
        if im_crop is not None:
            I = I[im_crop[0]:im_crop[1], im_crop[2]:im_crop[3]]

        # find pupil in frame
        pup = Pupil(I, threshold=pupil_threshold)
        data['pupil_list'].append(pup)

        # Extract the iris segment
        P = iris_transform(I, pup, window_height, theta_resolution=transform_resolution)

        # Find the torsion
        t = xcorr2d(P, reference_window, interp_start, resolution=interp_resolution, threshold=interp_threshold, torsion_mode='interp')
        data['torsion'].append(t)

        if verborose:
            print('Elapsed Time: {}s'.format(round(time.time() - start_time,2)), sep=' ', end='\r', flush=True)

    if verborose:
        print('Duration:', time.time() - start_time)

    # Save results
    obj = Data('_subset_interpolation', file_path)
    obj.set(data['torsion'], start_frame=start_frame, pupil_list=data['pupil_list'], metadata=metadata)
    obj.save()

    if verborose:
        print('Saving {} {} results.')