forked from jgrewe/efish_tracking
[distance_calibration] sequence checkerboard detection
This commit is contained in:
parent
8199fe4c7f
commit
0488ca6e64
@ -1,2 +1,3 @@
|
|||||||
from .image_marker import ImageMarker, MarkerTask
|
from .image_marker import ImageMarker, MarkerTask
|
||||||
from .tracking_result import TrackingResult
|
from .tracking_result import TrackingResult
|
||||||
|
from .distance_calibration import DistanceCalibration
|
@ -1,17 +1,21 @@
|
|||||||
from cv2 import calibrationMatrixValues
|
from multiprocessing import allow_connection_pickling
|
||||||
|
from turtle import left
|
||||||
|
from cv2 import MARKER_TRIANGLE_UP, calibrationMatrixValues, threshold
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import cv2
|
import cv2
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from IPython import embed
|
from IPython import embed
|
||||||
|
from etrack import MarkerTask, ImageMarker
|
||||||
|
|
||||||
|
|
||||||
class DistanceCalibration:
|
class DistanceCalibration():
|
||||||
|
|
||||||
def __init__(self, file_name, x_0=95, y_0=185, cam_dist=1.36, width=1.35, height=0.805, width_pixel=1975, height_pixel=1375, checkerboard_width=0.24, checkerboard_height=0.18,
|
def __init__(self, file_name, frame_number, x_0=154, y_0=1318, cam_dist=1.36, width=1.35, 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, rectangle_width=0.024, rectangle_height=0.0225, rectangle_width_pixel=100, rectangle_height_pixel=90,
|
checkerboard_width_pixel=500, checkerboard_height_pixel=350, rectangle_width=0.024, rectangle_height=0.0225, rectangle_width_pixel=100, rectangle_height_pixel=90,
|
||||||
rectangle_count_width=9, rectangle_count_height=7) -> None:
|
rectangle_count_width=9, rectangle_count_height=7) -> None:
|
||||||
|
super().__init__()
|
||||||
# aktualisieren
|
# 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.
|
"""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.
|
||||||
|
|
||||||
@ -21,9 +25,9 @@ class DistanceCalibration:
|
|||||||
y_0 (int, optional): Y-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.
|
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.
|
width (int, optional): Width in meter from one lightened corner of the tank to the other. Defaults to 1.35.
|
||||||
heigth (int, optional): Height 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.
|
width_pixel (int, optional): Width in pixel from one lightened corner of the tank to the other. Defaults to 1975.
|
||||||
height_pixel (int, optional): Heigth in pixel from one lightened corner of the tank to the other. Defaults to 1375.
|
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_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_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_width (int, optional): Number of black rectangles over the width of the whole checkerboard. Defaults to 9.
|
||||||
@ -47,15 +51,25 @@ class DistanceCalibration:
|
|||||||
self._x_factor = self.width / self.width_pix # m/pix
|
self._x_factor = self.width / self.width_pix # m/pix
|
||||||
self._y_factor = self.height / self.height_pix # m/pix
|
self._y_factor = self.height / self.height_pix # m/pix
|
||||||
|
|
||||||
# properties
|
self.mark_crop_positions
|
||||||
|
self.threshold_crossings
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def x_0(self):
|
def x_0(self):
|
||||||
return self._x_0
|
return self._x_0
|
||||||
|
|
||||||
|
@x_0.setter
|
||||||
|
def x_0(self, value):
|
||||||
|
self._x_0 = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y_0(self):
|
def y_0(self):
|
||||||
return self._y_0
|
return self._y_0
|
||||||
|
|
||||||
|
@y_0.setter
|
||||||
|
def y_0(self, value):
|
||||||
|
self._y_0 = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cam_dist(self):
|
def cam_dist(self):
|
||||||
return self._cam_dist
|
return self._cam_dist
|
||||||
@ -64,18 +78,50 @@ class DistanceCalibration:
|
|||||||
def width(self):
|
def width(self):
|
||||||
return self._width
|
return self._width
|
||||||
|
|
||||||
|
@width.setter
|
||||||
|
def width(self, value):
|
||||||
|
self._width = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def height(self):
|
def height(self):
|
||||||
return self._height
|
return self._height
|
||||||
|
|
||||||
|
@height.setter
|
||||||
|
def height(self, value):
|
||||||
|
self._height = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def width_pix(self):
|
def width_pix(self):
|
||||||
return self._width_pix
|
return self._width_pix
|
||||||
|
|
||||||
|
@width_pix.setter
|
||||||
|
def width_pix(self, value):
|
||||||
|
self._width_pix = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def height_pix(self):
|
def height_pix(self):
|
||||||
return self._height_pix
|
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
|
@property
|
||||||
def x_factor(self):
|
def x_factor(self):
|
||||||
return self._x_factor
|
return self._x_factor
|
||||||
@ -85,43 +131,86 @@ class DistanceCalibration:
|
|||||||
return self._y_factor
|
return self._y_factor
|
||||||
|
|
||||||
|
|
||||||
def crop_movie():
|
def mark_crop_positions(self):
|
||||||
if not os.path.exists(filename):
|
task = MarkerTask("crop area", ["bottom left corner", "top left corner", "top right corner", "bottom right corner"], "Mark crop area")
|
||||||
raise IOError("file %s does not exist!" % filename)
|
im = ImageMarker([task])
|
||||||
video = cv2.VideoCapture()
|
|
||||||
video.open(filename)
|
marker_positions = im.mark_movie(file_name, frame_number)
|
||||||
frame_counter = 0
|
print(marker_positions)
|
||||||
success = True
|
|
||||||
frame = None
|
np.save('marker_positions', marker_positions)
|
||||||
while success and frame_counter <= frame_number: # iterating until frame_counter == frame_number --> success (True)
|
|
||||||
print("Reading frame: %i" % frame_counter, end="\r")
|
return marker_positions
|
||||||
success, frame = video.read()
|
|
||||||
frame_counter += 1
|
|
||||||
if success:
|
|
||||||
self._fig.gca().imshow(frame) # plot wanted frame of video
|
|
||||||
else:
|
|
||||||
print("Could not read frame number %i either failed to open movie or beyond maximum frame number!" % frame_number)
|
|
||||||
return []
|
|
||||||
plt.ion() # turn on interactive mode
|
|
||||||
plt.show(block=False) # block=False allows to continue interact in terminal while the figure is open
|
|
||||||
|
|
||||||
self._task_index = -1
|
def crop_frame(self, frame, marker_positions):
|
||||||
if len(self._tasks) > 0:
|
|
||||||
self._next_task()
|
|
||||||
|
|
||||||
while not self._tasks_done:
|
bottom_left = marker_positions[0]['bottom left corner']
|
||||||
plt.pause(0.250)
|
bottom_right = marker_positions[0]['bottom right corner']
|
||||||
if self._interrupt:
|
top_left = marker_positions[0]['top left corner']
|
||||||
return []
|
top_right = marker_positions[0]['top right corner']
|
||||||
|
|
||||||
self._fig.gca().set_title("All set and done!\n Window will close in 2s")
|
left_bound = int(np.mean([bottom_left[0], top_left[0]]))
|
||||||
self._fig.canvas.draw()
|
right_bound = int(np.mean([bottom_right[0], top_right[0]]))
|
||||||
plt.pause(2.0)
|
top_bound = int(np.mean([top_left[1], top_right[1]]))
|
||||||
plt.close()
|
bottom_bound = int(np.mean([bottom_left[1], bottom_right[1]]))
|
||||||
return [t.marker_positions for t in self._tasks]
|
|
||||||
|
|
||||||
|
crop_frame = frame[top_bound:bottom_bound, left_bound:right_bound]
|
||||||
|
crop_frame = np.mean(crop_frame, axis=2)
|
||||||
|
|
||||||
|
frame_width = np.mean(crop_frame,axis=0)
|
||||||
|
frame_height = np.mean(crop_frame,axis=1)
|
||||||
|
|
||||||
|
diff_width = np.diff(frame_width)
|
||||||
|
diff_height = np.diff(frame_height)
|
||||||
|
|
||||||
|
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(self, data, threshold_factor):
|
||||||
|
lower_threshold = np.min(data) / threshold_factor
|
||||||
|
upper_threshold = np.max(data) / threshold_factor
|
||||||
|
|
||||||
|
lower_crossings = np.diff(data < lower_threshold, prepend=False)
|
||||||
|
upper_crossings = np.diff(data > upper_threshold, append=False)
|
||||||
|
|
||||||
|
lower_crossings_indices = np.argwhere(lower_crossings)
|
||||||
|
upper_crossings_indices = np.argwhere(upper_crossings)
|
||||||
|
|
||||||
|
half_window_size = 10
|
||||||
|
lower_peaks = []
|
||||||
|
upper_peaks = []
|
||||||
|
for lower_idx in lower_crossings_indices:
|
||||||
|
if lower_idx < half_window_size:
|
||||||
|
half_window_size = lower_idx
|
||||||
|
window = data[lower_idx[0] - int(half_window_size):lower_idx[0] + int(half_window_size)]
|
||||||
|
min_window = np.min(window)
|
||||||
|
min_idx = np.where(data == min_window)
|
||||||
|
lower_peaks.append(min_idx)
|
||||||
|
|
||||||
|
for upper_idx in upper_crossings_indices:
|
||||||
|
if upper_idx < half_window_size:
|
||||||
|
half_window_size = upper_idx
|
||||||
|
window = data[upper_idx[0] - int(half_window_size) : upper_idx[0] + int(half_window_size)]
|
||||||
|
|
||||||
|
max_window = np.max(window)
|
||||||
|
max_idx = np.where(data == max_window)
|
||||||
|
upper_peaks.append(max_idx)
|
||||||
|
|
||||||
|
lower_peaks = np.unique(lower_peaks)
|
||||||
|
upper_peaks = np.unique(upper_peaks)
|
||||||
|
|
||||||
|
return lower_peaks, upper_peaks
|
||||||
|
|
||||||
|
|
||||||
|
def detect_checkerboard(self, filename, frame_number, marker_positions):
|
||||||
|
|
||||||
def mark_checkerboard(self, filename, frame_number=10):
|
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise IOError("file %s does not exist!" % filename)
|
raise IOError("file %s does not exist!" % filename)
|
||||||
video = cv2.VideoCapture()
|
video = cv2.VideoCapture()
|
||||||
@ -139,47 +228,81 @@ class DistanceCalibration:
|
|||||||
print("Reading frame: %i" % frame_counter, end="\r")
|
print("Reading frame: %i" % frame_counter, end="\r")
|
||||||
success, frame = video.read()
|
success, frame = video.read()
|
||||||
frame_counter += 1
|
frame_counter += 1
|
||||||
width_mean = np.mean(frame,axis=1)
|
|
||||||
crop_width_mean = width_mean[x_0:width_pix]
|
|
||||||
|
|
||||||
height_mean = np.mean(frame,axis=0)
|
marker_positions = np.load('marker_positions.npy', allow_pickle=True)
|
||||||
crop_height_mean = height_mean[y_0:height_pix]
|
|
||||||
# HELLO, here you at
|
frame_width, frame_height, diff_width, diff_height, _, _ = dc.crop_frame(frame, marker_positions)
|
||||||
|
|
||||||
|
# y-axis is inverted..
|
||||||
|
|
||||||
|
thresh_fact = 7
|
||||||
|
lci_width, uci_width = dc.threshold_crossings(diff_width, threshold_factor=thresh_fact)
|
||||||
|
lci_height, uci_height = dc.threshold_crossings(diff_height, threshold_factor=thresh_fact)
|
||||||
|
|
||||||
|
print('lower crossings:', lci_width)
|
||||||
|
print('upper crossings:', uci_width)
|
||||||
|
|
||||||
|
# make function for this
|
||||||
|
zip_list = []
|
||||||
|
for zl in lci_width:
|
||||||
|
zip_list.append(zl)
|
||||||
|
for zu in uci_width:
|
||||||
|
zip_list.append(zu)
|
||||||
|
|
||||||
|
zip_list = np.sort(zip_list)
|
||||||
|
|
||||||
|
sequence = []
|
||||||
|
for z in zip_list:
|
||||||
|
if z in lci_width:
|
||||||
|
sequence.append('down')
|
||||||
|
else:
|
||||||
|
sequence.append('up')
|
||||||
|
print('sequence:', sequence)
|
||||||
|
|
||||||
|
if sequence == ['up', 'down', 'up', 'down']:
|
||||||
|
print('in middle')
|
||||||
|
# first down, second up are edges of checkerboard
|
||||||
|
elif sequence == ['up', 'up', 'down']:
|
||||||
|
print('at left')
|
||||||
|
# first and second up are edges of checkerboard
|
||||||
|
else:
|
||||||
|
print('at right')
|
||||||
|
# first and second down are edges of checkerboard
|
||||||
|
|
||||||
|
# find mistake in threshold detection (_7.mp4) where two detections at side (by thresh factor)
|
||||||
|
# find which indices (=pixels) represent edges of checkerboard by the corresponding sequence of ups and downs
|
||||||
|
# both for width and height
|
||||||
|
# assign x and y positions for the checkerboard corners
|
||||||
|
|
||||||
|
# pixel to meter factor for default position with checkerboard in center of tank underneath camera
|
||||||
|
|
||||||
|
plt.plot(diff_width)
|
||||||
|
plt.axhline(np.min(diff_width) / thresh_fact)
|
||||||
|
plt.axhline(np.max(diff_width) / thresh_fact)
|
||||||
|
for l in lci_width:
|
||||||
|
plt.axvline(l, color='yellow')
|
||||||
|
for u in uci_width:
|
||||||
|
plt.axvline(u, color='green')
|
||||||
|
# plt.plot(frame_height)
|
||||||
|
plt.plot(frame_width)
|
||||||
|
plt.show()
|
||||||
embed()
|
embed()
|
||||||
quit()
|
quit()
|
||||||
if success:
|
|
||||||
self._fig.gca().imshow(frame) # plot wanted frame of video
|
|
||||||
else:
|
|
||||||
print("Could not read frame number %i either failed to open movie or beyond maximum frame number!" % frame_number)
|
|
||||||
return []
|
|
||||||
plt.ion() # turn on interactive mode
|
|
||||||
plt.show(block=False) # block=False allows to continue interact in terminal while the figure is open
|
|
||||||
|
|
||||||
self._task_index = -1
|
# rotation angle
|
||||||
if len(self._tasks) > 0:
|
|
||||||
self._next_task()
|
|
||||||
|
|
||||||
while not self._tasks_done:
|
|
||||||
plt.pause(0.250)
|
|
||||||
if self._interrupt:
|
|
||||||
return []
|
|
||||||
|
|
||||||
self._fig.gca().set_title("All set and done!\n Window will close in 2s")
|
if __name__ == "__main__":
|
||||||
self._fig.canvas.draw()
|
file_name = "/home/efish/etrack/videos/2022.03.28_7.mp4"
|
||||||
plt.pause(2.0)
|
frame_number = 10
|
||||||
plt.close()
|
dc = DistanceCalibration(file_name=file_name, frame_number=frame_number)
|
||||||
return [t.marker_positions for t in self._tasks]
|
|
||||||
|
|
||||||
|
# marker_positions = dc.mark_crop_positions()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
dc.detect_checkerboard(file_name, frame_number=frame_number, marker_positions=np.load('marker_positions.npy', allow_pickle=True))
|
||||||
vid2 = "/home/efish/etrack/videos/2022.03.28_3.mp4"
|
|
||||||
calibration_task = DistanceCalibration(vid2)
|
|
||||||
dc = DistanceCalibration(calibration_task)
|
|
||||||
dc.mark_checkerboard(vid2, 10)
|
|
||||||
|
|
||||||
# print(sys.argv[0])
|
# print(sys.argv[0])
|
||||||
# print (sys.argv[1])
|
# print (sys.argv[1])
|
||||||
# vid1 = sys.argv[1]
|
# vid1 = sys.argv[1]
|
||||||
|
|
||||||
embed()
|
# embed()
|
@ -20,7 +20,7 @@ class ImageMarker:
|
|||||||
self._fig.canvas.mpl_connect('close_event', self._fig_close_event)
|
self._fig.canvas.mpl_connect('close_event', self._fig_close_event)
|
||||||
self._fig.canvas.mpl_connect('key_press_event', self._key_press_event)
|
self._fig.canvas.mpl_connect('key_press_event', self._key_press_event)
|
||||||
|
|
||||||
def mark_movie(self, filename, frame_number=0):
|
def mark_movie(self, filename, frame_number=10):
|
||||||
""" Interactive GUI to mark the corners of the tank. A specific frame of the video can be chosen. Returns marker positions.
|
""" Interactive GUI to mark the corners of the tank. A specific frame of the video can be chosen. Returns marker positions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -161,7 +161,7 @@ if __name__ == "__main__":
|
|||||||
im = ImageMarker(tasks)
|
im = ImageMarker(tasks)
|
||||||
|
|
||||||
vid1 = "/home/efish/efish_tracking/efish_tracking3-Xaver-2022-03-21/videos/2022.01.12_3DLC_resnet50_efish_tracking3Mar21shuffle1_300000_labeled.mp4"
|
vid1 = "/home/efish/efish_tracking/efish_tracking3-Xaver-2022-03-21/videos/2022.01.12_3DLC_resnet50_efish_tracking3Mar21shuffle1_300000_labeled.mp4"
|
||||||
marker_positions = im.mark_movie(vid1, 100)
|
marker_positions = im.mark_movie(vid1, 10)
|
||||||
print(marker_positions)
|
print(marker_positions)
|
||||||
|
|
||||||
# print(sys.argv[0])
|
# print(sys.argv[0])
|
||||||
|
Loading…
Reference in New Issue
Block a user