From 08a4abda6200cbff4ec1aad4b66f6f574d9a98b7 Mon Sep 17 00:00:00 2001
From: Xaver Roos <xaver.roos@gmail.com>
Date: Fri, 29 Apr 2022 11:51:48 +0200
Subject: [PATCH] [distance_factor_calculation] towards distance factor
 interpolation

---
 etrack/calibration_functions.py |  41 ++++--
 etrack/distance_calibration.py  | 214 ++++++++++++++++----------------
 etrack/image_marker.py          |   2 +-
 3 files changed, 140 insertions(+), 117 deletions(-)

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):