efish_tracking/etrack/calibration_functions.py

163 lines
6.9 KiB
Python

from turtle import left
import matplotlib.pyplot as plt
import numpy as np
from IPython import embed
def crop_frame(frame, marker_positions):
# load the four marker positions
bottom_left = marker_positions[0]['bottom left corner']
bottom_right = marker_positions[0]['bottom right corner']
top_left = marker_positions[0]['top left corner']
top_right = marker_positions[0]['top right corner']
# define boundaries of frame, taken by average of points on same line but slightly different pixel values
left_bound = int(np.mean([bottom_left[0], top_left[0]]))
right_bound = int(np.mean([bottom_right[0], top_right[0]]))
top_bound = int(np.mean([top_left[1], top_right[1]]))
bottom_bound = int(np.mean([bottom_left[1], bottom_right[1]]))
# crop the frame by boundary values
crop_frame = frame[top_bound:bottom_bound, left_bound:right_bound]
crop_frame = np.mean(crop_frame, axis=2) # mean over 3rd dimension (RGB/color values)
# mean over short or long side of the frame corresponding to x or y axis of picture
frame_width = np.mean(crop_frame,axis=0)
frame_height = np.mean(crop_frame,axis=1)
# differences of color values lying next to each other --> derivation
diff_width = np.diff(frame_width)
diff_height = np.diff(frame_height)
# two x vectors for better plotting
x_width = np.arange(0, len(diff_width), 1)
x_height = np.arange(0, len(diff_height), 1)
return frame_width, frame_height, diff_width, diff_height, x_width, x_height
def rotation_angle():
pass
def threshold_crossings(data, threshold_factor):
# upper and lower threshold
median_data = np.median(data)
median_lower = median_data + np.min(data)
median_upper = np.max(data) - median_data
lower_threshold = median_lower / threshold_factor
upper_threshold = median_upper / threshold_factor
# array with values if data >/< than threshold = True or not
lower_crossings = np.diff(data < lower_threshold, prepend=False) # prepend: point after crossing
upper_crossings = np.diff(data > upper_threshold, append=False) # append: point before crossing
# indices where crossings are
lower_crossings_indices = np.argwhere(lower_crossings)
upper_crossings_indices = np.argwhere(upper_crossings)
# sort out several crossings of same edge of checkerboard (due to noise)
half_window_size = 10
lower_peaks = []
upper_peaks = []
for lower_idx in lower_crossings_indices: # for every lower crossing..
if lower_idx < half_window_size: # ..if indice smaller than window size near indice 0
half_window_size = lower_idx
lower_window = data[lower_idx[0] - int(half_window_size):lower_idx[0] + int(half_window_size)] # create data window from -window_size to +window_size
min_window = np.min(lower_window) # take minimum of window
min_idx = np.where(data == min_window) # find indice where minimum is
lower_peaks.append(min_idx) # append to list
for upper_idx in upper_crossings_indices: # same for upper crossings with max of window
if upper_idx < half_window_size:
half_window_size = upper_idx
upper_window = data[upper_idx[0] - int(half_window_size) : upper_idx[0] + int(half_window_size)]
max_window = np.max(upper_window)
max_idx = np.where(data == max_window)
upper_peaks.append(max_idx)
# if several crossings create same peaks due to overlapping windows, only one (unique) will be taken
lower_peaks = np.unique(lower_peaks)
upper_peaks = np.unique(upper_peaks)
return lower_peaks, upper_peaks
def checkerboard_position(lower_crossings_indices, upper_crossings_indices):
"""Take crossing positions to generate a characteristic sequence for a corresponding position of the checkerboard inside the frame.
Positional description has to be interpreted depending on the input data.
Args:
lower_crossings_indices: Indices where lower threshold was crossed by derivation data.
upper_crossings_indices: Indices where upper threshold was crossed by derivation data
Returns:
checkerboard_position: General position where the checkerboard lays inside the frame along the axis of the input data.
"""
# create zipped list with both indices
zip_list = []
for zl in lower_crossings_indices:
zip_list.append(zl)
for zu in upper_crossings_indices:
zip_list.append(zu)
zip_list = np.sort(zip_list) # order by indice
# compare and assign zipped list to original indices lists and corresponding direction (to upper or lower threshold)
sequence = []
for z in zip_list:
if z in lower_crossings_indices:
sequence.append('down')
else:
sequence.append('up')
print('sequence:', sequence)
# depending on order of crossings through upper or lower treshold, we get a characteristic sequence for a position of the checkerboard in the frame
if sequence == ['up', 'down', 'up', 'down']: # first down, second up are edges of checkerboard
print('in middle')
checkerboard_position = 'middle'
left_checkerboard_edge = zip_list[1]
right_checkerboard_edge = zip_list[2]
elif sequence == ['up', 'up', 'down']: # first and second up are edges of checkerboard
print('at left')
checkerboard_position = 'left'
left_checkerboard_edge = zip_list[0]
right_checkerboard_edge = zip_list[1]
else: # first and second down are edges of checkerboard
print('at right')
checkerboard_position = 'right'
left_checkerboard_edge = zip_list[1]
right_checkerboard_edge = zip_list[2]
return checkerboard_position, left_checkerboard_edge, right_checkerboard_edge # position of checkerboard then will be returned
def filter_data(data, n):
"""Filter/smooth data with kernel of length n.
Args:
data: Raw data.
n: Number of datapoints the mean gets computed over.
Returns:
filtered_data: Filtered data.
"""
new_data = np.zeros(len(data)) # empty vector where data will be put in in the following steps
for k in np.arange(0, len(data) - n):
kk = int(k)
f = np.mean(data[kk:kk+n]) # mean over data over window from kk to kk+n
kkk = int(kk+n / 2) # position where mean datapoint will be placed (so to say)
if k == 0:
new_data[:kkk] = f
new_data[kkk] = f # assignment of value to datapoint
new_data[kkk:] = f
for nd in new_data[0:n-1]: # correction of left boundary effects (boundaries up to length of n were same number)
nd_idx = np.argwhere(nd)
new_data[nd_idx] = data[nd_idx]
for nd in new_data[-1 - (n-1):-1]: # same as above, correction of right boundary effect
nd_idx = np.argwhere(nd)
new_data[nd_idx] = data[nd_idx]
return new_data