diff --git a/etrack/calibration_functions.py b/etrack/calibration_functions.py index a42da78..a1341f4 100644 --- a/etrack/calibration_functions.py +++ b/etrack/calibration_functions.py @@ -2,10 +2,22 @@ from turtle import left import matplotlib.pyplot as plt import numpy as np from IPython import embed +from etrack import MarkerTask, ImageMarker -def crop_frame(frame, marker_positions): - # load the four marker positions +def mark_crop_positions(self): + task = MarkerTask("crop area", ["bottom left corner", "top left corner", "top right corner", "bottom right corner"], "Mark crop area") + im = ImageMarker([task]) + + marker_positions = im.mark_movie(file_name, frame_number) + print(marker_positions) + + np.save('marker_positions', marker_positions) + + return marker_positions + + +def assign_marker_positions(marker_positions): bottom_left_x = marker_positions[0]['bottom left corner'][0] bottom_left_y = marker_positions[0]['bottom left corner'][1] bottom_right_x = marker_positions[0]['bottom right corner'][0] @@ -14,6 +26,21 @@ def crop_frame(frame, marker_positions): top_left_y = marker_positions[0]['top left corner'][1] top_right_x = marker_positions[0]['top right corner'][0] top_right_y = marker_positions[0]['top right corner'][1] + return bottom_left_x, bottom_left_y, bottom_right_x, bottom_right_y, top_left_x, top_left_y, top_right_x, top_right_y + + +def assign_checkerboard_positions(checkerboard_marker_positions): + checkerboard_top_right = checkerboard_marker_positions[0]['top right corner'] + checkerboard_top_left = checkerboard_marker_positions[0]['top left corner'] + checkerboard_bottom_right = checkerboard_marker_positions[0]['bottom right corner'] + checkerboard_bottom_left = checkerboard_marker_positions[0]['bottom left corner'] + return checkerboard_top_right, checkerboard_top_left, checkerboard_bottom_right, checkerboard_bottom_left + + +def crop_frame(frame, marker_positions): + + # load the four marker positions + bottom_left_x, bottom_left_y, bottom_right_x, bottom_right_y, top_left_x, top_left_y, top_right_x, top_right_y = assign_marker_positions(marker_positions) # define boundaries of frame, taken by average of points on same line but slightly different pixel values left_bound = int(np.mean([bottom_left_x, top_left_x])) @@ -22,12 +49,12 @@ def crop_frame(frame, marker_positions): bottom_bound = int(np.mean([bottom_left_y, bottom_right_y])) # 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) + cropped_frame = frame[top_bound:bottom_bound, left_bound:right_bound] + cropped_frame = np.mean(cropped_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) + frame_width = np.mean(cropped_frame,axis=0) + frame_height = np.mean(cropped_frame,axis=1) # differences of color values lying next to each other --> derivation diff_width = np.diff(frame_width) @@ -37,7 +64,7 @@ def crop_frame(frame, marker_positions): x_width = np.arange(0, len(diff_width), 1) x_height = np.arange(0, len(diff_height), 1) - return crop_frame, frame_width, frame_height, diff_width, diff_height, x_width, x_height + return cropped_frame, frame_width, frame_height, diff_width, diff_height, x_width, x_height def rotation_angle(): pass diff --git a/etrack/distance_calibration.py b/etrack/distance_calibration.py index 10e951e..0ae4d5c 100644 --- a/etrack/distance_calibration.py +++ b/etrack/distance_calibration.py @@ -7,9 +7,10 @@ import numpy as np import cv2 import os import sys +import glob from IPython import embed -from etrack import MarkerTask, ImageMarker -from calibration_functions import crop_frame, threshold_crossings, checkerboard_position, filter_data +from calibration_functions import * + class DistanceCalibration(): @@ -17,24 +18,7 @@ class DistanceCalibration(): def __init__(self, file_name, frame_number, x_0=154, y_0=1318, cam_dist=1.36, tank_width=1.35, tank_height=0.805, width_pixel=1900, height_pixel=200, checkerboard_width=0.24, checkerboard_height=0.18, checkerboard_width_pixel=500, checkerboard_height_pixel=350) -> None: super().__init__() - # aktualisieren - """Calibration of the dimensions of the tank. Conversion of pixel into meter. Width refers to the "x-axis", height to the "y-axis" of the tank. - - Args: - file_name (_type_): _description_ - x_0 (int, optional): X-value of the "origin" of the tank. Defaults to 0. - y_0 (int, optional): Y-value of the "origin" of the tank. Defaults to 0. - cam_dist (int, optional): Distance of camera lense to tank floor. Defaults to 1.36. - width (int, optional): Width in meter from one lightened corner of the tank to the other. Defaults to 1.35. - height (int, optional): Height in meter from one lightened corner of the tank to the other. Defaults to 1.35. - width_pixel (int, optional): Width in pixel from one lightened corner of the tank to the other. Defaults to 1975. - height_pixel (int, optional): Height in pixel from one lightened corner of the tank to the other. Defaults to 1375. - rectangle_width (float, optional): Width of one black or corresponding white rectangle of the checkerboard. Defaults to 0.024. - rectangle_height (float, optional): Height of one black or corresponding white rectangle of the checkerboard. Defaults to 0.0225. - rectangle_count_width (int, optional): Number of black rectangles over the width of the whole checkerboard. Defaults to 9. - rectangle_count_height (int, optional): Number of black rectangles over the height of the whole checkerboard. Defaults to 7. - """ - + self._file_name = file_name self._x_0 = x_0 self._y_0 = y_0 @@ -50,24 +34,18 @@ class DistanceCalibration(): self._x_factor = tank_width / width_pixel # m/pix self._y_factor = tank_height / height_pixel # m/pix - self.distance_calculation + self.distance_factor_calculation + self.mark_crop_positions + # if needed include setter: @y_0.setter def y_0(self, value): self._y_0 = value @property def x_0(self): return self._x_0 - - @x_0.setter - def x_0(self, value): - self._x_0 = value @property def y_0(self): return self._y_0 - @y_0.setter - def y_0(self, value): - self._y_0 = value - @property def cam_dist(self): return self._cam_dist @@ -76,50 +54,26 @@ class DistanceCalibration(): def width(self): return self._width - @width.setter - def width(self, value): - self._width = value - @property def height(self): return self._height - @height.setter - def height(self, value): - self._height = value - @property def width_pix(self): return self._width_pix - @width_pix.setter - def width_pix(self, value): - self._width_pix = value - @property def height_pix(self): return self._height_pix - @height_pix.setter - def height_pix(self, value): - self._height_pix_ = value - @property def cb_width(self): return self._cb_width - - @cb_width.setter - def cb_width(self, value): - self._cb_width = value - + @property def cb_height(self): return self._cb_height - @cb_height.setter - def cb_height(self, value): - self._cb_height = value - @property def x_factor(self): return self._x_factor @@ -127,22 +81,22 @@ class DistanceCalibration(): @property def y_factor(self): return self._y_factor - + def mark_crop_positions(self): - task = MarkerTask("crop area", ["bottom left corner", "top left corner", "top right corner", "bottom right corner"], "Mark crop area") - im = ImageMarker([task]) - - marker_positions = im.mark_movie(file_name, frame_number) - print(marker_positions) + task = MarkerTask("crop area", ["bottom left corner", "top left corner", "top right corner", "bottom right corner"], "Mark crop area") + im = ImageMarker([task]) + + marker_positions = im.mark_movie(file_name, frame_number) + print(marker_positions) - np.save('marker_positions', marker_positions) + np.save('marker_positions', marker_positions) - return marker_positions + return marker_positions def detect_checkerboard(self, filename, frame_number, marker_positions): - + # load frame if not os.path.exists(filename): raise IOError("file %s does not exist!" % filename) video = cv2.VideoCapture() @@ -150,11 +104,6 @@ class DistanceCalibration(): frame_counter = 0 success = True frame = None - - x_0 = self._x_0 - y_0 = self._y_0 - width_pix = self._width_pix - height_pix = self._height_pix while success and frame_counter <= frame_number: # iterating until frame_counter == frame_number --> success (True) print("Reading frame: %i" % frame_counter, end="\r") @@ -162,15 +111,23 @@ class DistanceCalibration(): frame_counter += 1 marker_positions = np.load('marker_positions.npy', allow_pickle=True) # load saved numpy marker positions file - bottom_left_marker = marker_positions[0]['bottom left corner'] - bottom_right_marker= marker_positions[0]['bottom right corner'] - top_left_marker = marker_positions[0]['top left corner'] - top_right_marker = marker_positions[0]['top right corner'] - + # care: y-axis is inverted, top values are low, bottom values are high - croped_frame, frame_width, frame_height, diff_width, diff_height, _, _ = crop_frame(frame, marker_positions) # crop frame to given marker positions + cropped_frame, frame_width, frame_height, diff_width, diff_height, _, _ = crop_frame(frame, marker_positions) # crop frame to given marker positions + bottom_left_x = 0 + bottom_left_y = np.shape(cropped_frame)[0] + bottom_right_x = np.shape(cropped_frame)[1] + bottom_right_y = np.shape(cropped_frame)[0] + top_left_x = 0 + top_left_y = 0 + top_right_x = np.shape(cropped_frame)[1] + top_right_y = 0 + + cropped_marker_positions = [{'bottom left corner': (bottom_left_x, bottom_left_y), 'top left corner': (top_left_x, top_left_y), + 'top right corner': (top_right_x, top_right_y), 'bottom right corner': (bottom_right_x, bottom_right_y)}] + thresh_fact = 7 # factor by which the min/max is divided to calculate the upper and lower thresholds # filtering/smoothing of data using kernel with n datapoints @@ -188,49 +145,60 @@ class DistanceCalibration(): # position of checkerboard in width print('width..') width_position, left_width_position, right_width_position = checkerboard_position(lci_width, uci_width) + # position of checkerboard in height print('height..') height_position, left_height_position, right_height_position = checkerboard_position(lci_height, uci_height) # left height refers to top, right height to bottom + if width_position == 'left' and height_position == 'left': + checkerboard_position_tank = 'top left' + elif width_position == 'left' and height_position == 'right': + checkerboard_position_tank = 'bottom left' + elif width_position == 'right' and height_position == 'right': + checkerboard_position_tank = 'bottom right' + elif width_position == 'right' and height_position == 'left': + checkerboard_position_tank = 'top right' + else: + checkerboard_position_tank = 'middle' + + print(checkerboard_position_tank) + # final corner positions of checkerboard - top_left = np.array([left_width_position, left_height_position]) - top_right = np.array([right_width_position, left_height_position]) - bottom_left = np.array([left_width_position, right_height_position]) - bottom_right = np.array([right_width_position, right_height_position]) + checkerboard_marker_positions = [{'bottom left corner': (left_width_position, right_height_position), 'top left corner': (left_width_position, left_height_position), + 'top right corner': (right_width_position, left_height_position), 'bottom right corner': (right_width_position, right_height_position)}] - print(top_left, top_right, bottom_left, bottom_right) + print(checkerboard_marker_positions) - # fig, ax = plt.subplots() - # ax.imshow(croped_frame) - # for p in top_left, top_right, bottom_left, bottom_right: - # ax.scatter(p[0], p[1]) - # plt.show() - - # embed() - # quit() - - return top_left, top_right, bottom_left, bottom_right + checkerboard_top_right, checkerboard_top_left, checkerboard_bottom_right, checkerboard_bottom_left = assign_checkerboard_positions(checkerboard_marker_positions) + + fig, ax = plt.subplots() + ax.imshow(cropped_frame) + for p in checkerboard_top_left, checkerboard_top_right, checkerboard_bottom_left, checkerboard_bottom_right: + ax.scatter(p[0], p[1]) + ax.scatter(bottom_left_x, bottom_left_y) + ax.scatter(bottom_right_x, bottom_right_y) + ax.scatter(top_left_x, top_left_y) + ax.scatter(top_right_x, top_right_y) + plt.show() + + + return checkerboard_marker_positions, cropped_marker_positions, checkerboard_position_tank - def distance_calculation(self, top_left, top_right, bottom_left, bottom_right, marker_positions): + def distance_factor_calculation(self, checkerboard_marker_positions, marker_positions): + checkerboard_top_right, checkerboard_top_left, checkerboard_bottom_right, checkerboard_bottom_left = assign_checkerboard_positions(checkerboard_marker_positions) + checkerboard_width = 0.24 checkerboard_height = 0.18 - checkerboard_width_pixel = top_right[0] - top_left[0] - checkerboard_height_pixel = bottom_right[1] - top_right[1] + checkerboard_width_pixel = checkerboard_top_right[0] - checkerboard_top_left[0] + checkerboard_height_pixel = checkerboard_bottom_right[1] - checkerboard_top_right[1] x_factor = checkerboard_width / checkerboard_width_pixel y_factor = checkerboard_height / checkerboard_height_pixel - bottom_left_x = marker_positions[0]['bottom left corner'][0] - bottom_left_y = marker_positions[0]['bottom left corner'][1] - bottom_right_x = marker_positions[0]['bottom right corner'][0] - bottom_right_y = marker_positions[0]['bottom right corner'][1] - top_left_x = marker_positions[0]['top left corner'][0] - top_left_y = marker_positions[0]['top left corner'][1] - top_right_x = marker_positions[0]['top right corner'][0] - top_right_y = marker_positions[0]['top right corner'][1] + bottom_left_x, bottom_left_y, bottom_right_x, bottom_right_y, top_left_x, top_left_y, top_right_x, top_right_y = assign_marker_positions(marker_positions) tank_width_pixel = np.mean([bottom_right_x - bottom_left_x, top_right_x - top_left_x]) tank_height_pixel = np.mean([bottom_left_y - top_left_y, bottom_right_y - top_right_y]) @@ -239,23 +207,51 @@ class DistanceCalibration(): tank_height = tank_height_pixel * y_factor print(tank_width, tank_height) + + return x_factor, y_factor - embed() - quit() + + def distance_factor_interpolation(x_factors, y_factors): pass if __name__ == "__main__": - file_name = "/home/efish/etrack/videos/2022.03.28_3.mp4" - frame_number = 10 - dc = DistanceCalibration(file_name=file_name, frame_number=frame_number) - - # marker_positions = dc.mark_crop_positions() - top_left, top_right, bottom_left, bottom_right = dc.detect_checkerboard(file_name, frame_number=frame_number, marker_positions=np.load('marker_positions.npy', allow_pickle=True)) + all_x_factor = [] + all_y_factor = [] + all_checkerboard_position_tank = [] + for file_name in glob.glob("/home/efish/etrack/videos/*"): + # file_name = "/home/efish/etrack/videos/2022.03.28_4.mp4" + frame_number = 10 + dc = DistanceCalibration(file_name=file_name, frame_number=frame_number) + + dc.mark_crop_positions() + + checkerboard_marker_positions, cropped_marker_positions, checkerboard_position_tank = dc.detect_checkerboard(file_name, frame_number=frame_number, marker_positions=np.load('marker_positions.npy', allow_pickle=True)) + + x_factor, y_factor = dc.distance_factor_calculation(checkerboard_marker_positions, marker_positions=cropped_marker_positions) + + all_x_factor.append(x_factor) + all_y_factor.append(y_factor) + all_checkerboard_position_tank.append(checkerboard_position_tank) + + x_factors = np.load('x_factors.npy') + y_factors = np.load('y_factors.npy') + all_checkerboard_position_tank = np.load('all_checkerboard_position_tank.npy') - dc.distance_calculation(top_left, top_right, bottom_left, bottom_right, marker_positions = np.load('marker_positions.npy', allow_pickle=True)) + embed() + quit() + + # next up: distance calculation with angle + # is this needed or are current videos enough?: + # laying checkerboard at position directly above and below / left and right to centered checkerboard near edge of tank + # calculating x and y factor for centered checkerboard, then for the ones at the edge + # --> afterwards interpolate between them to have continuous factors for whole tank + # maybe smaller object in tank to have more accurate factor + + # make function to refine checkerboard detection at edges of tank by saying if no lower color values appears near edge --> checkerboard position then == corner of tank? + # # mark_crop_positions why failing plot at end? # with rectangles of checkerboard? diff --git a/etrack/image_marker.py b/etrack/image_marker.py index 4fb6322..128e2e4 100644 --- a/etrack/image_marker.py +++ b/etrack/image_marker.py @@ -65,7 +65,7 @@ class ImageMarker: self._fig.gca().set_title("All set and done!\n Window will close in 2s") self._fig.canvas.draw() plt.pause(2.0) - plt.close() + plt.close() #self._fig.gca().imshow(frame)) return [t.marker_positions for t in self._tasks] def _key_press_event(self, event):