|
@@ -2,7 +2,7 @@
|
|
|
"cells": [
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 1,
|
|
|
+ "execution_count": 38,
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
@@ -15,13 +15,22 @@
|
|
|
"import matplotlib.pyplot as plt\n",
|
|
|
"import multiprocessing as mp\n",
|
|
|
"\n",
|
|
|
- "from scipy import stats\n",
|
|
|
+ "from scipy import stats, signal\n",
|
|
|
"from situtils import FPSTimes"
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 93,
|
|
|
+ "execution_count": 60,
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": [
|
|
|
+ "# TODO - write an Agent class and unify position/HD tracking for multiple agents"
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": 56,
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
@@ -135,27 +144,37 @@
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def positions_in_px(self):\n",
|
|
|
+ " # a list of pairs [(Xi, Yi), ...] of actual positions for each agent tracked - in pixels\n",
|
|
|
" return NotImplemented\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def positions_in_m(self):\n",
|
|
|
+ " # a list of pairs [(Xi, Yi), ...] of actual positions for each agent tracked - in meters\n",
|
|
|
" return NotImplemented\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def contours(self):\n",
|
|
|
+ " return NotImplemented\n",
|
|
|
+ " \n",
|
|
|
+ " @property\n",
|
|
|
+ " def speeds(self):\n",
|
|
|
+ " return NotImplemented\n",
|
|
|
+ " \n",
|
|
|
+ " @property\n",
|
|
|
+ " def hds(self):\n",
|
|
|
" return NotImplemented"
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 96,
|
|
|
+ "execution_count": 57,
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
|
- "class PositionTrackerSingle(PositionTrackerBase):\n",
|
|
|
+ "class PositionTrackerSingleNOHD(PositionTrackerBase):\n",
|
|
|
" \n",
|
|
|
" def __init__(self, status, video_stream, cfg):\n",
|
|
|
- " super(PositionTrackerSingle, self).__init__(status, video_stream, cfg)\n",
|
|
|
+ " super(PositionTrackerSingleNOHD, self).__init__(status, video_stream, cfg)\n",
|
|
|
" \n",
|
|
|
" self._position = None\n",
|
|
|
" self._contour = None\n",
|
|
@@ -242,7 +261,7 @@
|
|
|
},
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 97,
|
|
|
+ "execution_count": 58,
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
@@ -370,374 +389,44 @@
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def contours(self): # always in pixels\n",
|
|
|
- " return [self._contour1, self._contour2] if self._contour1 is not None else None"
|
|
|
- ]
|
|
|
- },
|
|
|
- {
|
|
|
- "cell_type": "code",
|
|
|
- "execution_count": null,
|
|
|
- "metadata": {},
|
|
|
- "outputs": [],
|
|
|
- "source": []
|
|
|
- },
|
|
|
- {
|
|
|
- "cell_type": "code",
|
|
|
- "execution_count": null,
|
|
|
- "metadata": {},
|
|
|
- "outputs": [],
|
|
|
- "source": []
|
|
|
- },
|
|
|
- {
|
|
|
- "cell_type": "code",
|
|
|
- "execution_count": 91,
|
|
|
- "metadata": {},
|
|
|
- "outputs": [],
|
|
|
- "source": [
|
|
|
- "class ZZDoublePositionTracker(FPSTimes):\n",
|
|
|
- "\n",
|
|
|
- " default_cfg = {\n",
|
|
|
- " \"background_light\": \"background_light.png\",\n",
|
|
|
- " \"background_dark\": \"background_dark.png\",\n",
|
|
|
- " \"threshold_light\": 60,\n",
|
|
|
- " \"threshold_dark\": 30,\n",
|
|
|
- " \"min_blob_size\": 100,\n",
|
|
|
- " \"subtract\": 1,\n",
|
|
|
- " \"arena_x\": 522,\n",
|
|
|
- " \"arena_y\": 372,\n",
|
|
|
- " \"arena_radius\": 330,\n",
|
|
|
- " \"floor_radius\": 287,\n",
|
|
|
- " \"max_fps\": 50,\n",
|
|
|
- " \"file_path\": \"positions.csv\",\n",
|
|
|
- " \"contour_path\": \"contours.csv\",\n",
|
|
|
- " \"floor_r_in_meters\": 0.46,\n",
|
|
|
- " \"angle_compensation\": -90,\n",
|
|
|
- " \"flip_x\": True,\n",
|
|
|
- " \"flip_y\": False\n",
|
|
|
- " }\n",
|
|
|
- " \n",
|
|
|
- " def __init__(self, status, video_stream, cfg):\n",
|
|
|
- " super(DoublePositionTracker, self).__init__()\n",
|
|
|
- " \n",
|
|
|
- " self.status = status\n",
|
|
|
- " self.cfg = cfg\n",
|
|
|
- " self.video_stream = video_stream\n",
|
|
|
- " self.bg_light = cv2.imread(cfg['background_light'], 1)\n",
|
|
|
- " self.bg_dark = cv2.imread(cfg['background_dark'], 1)\n",
|
|
|
- " self.background = self.bg_light # light by default\n",
|
|
|
- " self.is_light = True\n",
|
|
|
- " self.positions_list_1, self.positions_list_2 = None, None\n",
|
|
|
- " self.dist_array = []\n",
|
|
|
- " self.pixel_size = cfg['floor_r_in_meters'] / float(cfg['floor_radius'])\n",
|
|
|
- " self.contour1, self.contour2 = [], []\n",
|
|
|
- " self.lr = None # linear regression of the contour\n",
|
|
|
- " self.stopped = False\n",
|
|
|
- "\n",
|
|
|
- " self.mask = np.zeros(shape=self.background.shape, dtype=\"uint8\")\n",
|
|
|
- " cv2.circle(self.mask, (cfg['arena_x'], cfg['arena_y']), cfg['arena_radius'], (255,255,255), -1)\n",
|
|
|
- "\n",
|
|
|
- " with open(cfg['file_path'], 'w') as f:\n",
|
|
|
- " f.write(\"time,x1,y1,x2,y2\\n\")\n",
|
|
|
- " with open(cfg['contour_path'], 'w') as f:\n",
|
|
|
- " f.write(\"x:y,...\\n\")\n",
|
|
|
- "\n",
|
|
|
- " def reload_background(self):\n",
|
|
|
- " self.bg_light = cv2.imread(self.cfg['background_light'], 1)\n",
|
|
|
- " self.bg_dark = cv2.imread(self.cfg['background_dark'], 1)\n",
|
|
|
- " self.background = self.bg_light if self.is_light else self.bg_dark\n",
|
|
|
- " print('Position tracker - background reloaded')\n",
|
|
|
- " \n",
|
|
|
- " def px_to_meters(self, x, y):\n",
|
|
|
- " x_m = float(self.cfg['arena_x'] - x) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " y_m = float(self.cfg['arena_y'] - y) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
- " return x_m, y_m\n",
|
|
|
- "\n",
|
|
|
- " def meters_to_px(self, x, y):\n",
|
|
|
- " x_m = self.cfg['arena_x'] - (x / self.pixel_size) * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " y_m = self.cfg['arena_y'] - (y / self.pixel_size) * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
- " return int(x_m), int(y_m)\n",
|
|
|
- "\n",
|
|
|
- " def correct_angle(self, phi):\n",
|
|
|
- " return (2*np.pi - phi) + np.deg2rad(self.cfg['angle_compensation'])\n",
|
|
|
- " \n",
|
|
|
- " def start(self):\n",
|
|
|
- " self._th = threading.Thread(target=self.update, args=())\n",
|
|
|
- " self._th.start()\n",
|
|
|
- " \n",
|
|
|
- " def stop(self):\n",
|
|
|
- " self.stopped = True\n",
|
|
|
- " self._th.join()\n",
|
|
|
- " print('Position tracker stopped')\n",
|
|
|
- " \n",
|
|
|
- " def update(self):\n",
|
|
|
- " next_frame = time.time() + 1.0/self.cfg['max_fps']\n",
|
|
|
- " \n",
|
|
|
- " while not self.stopped:\n",
|
|
|
- " frame = self.video_stream.read()\n",
|
|
|
- " if frame is None:\n",
|
|
|
- " time.sleep(0.05)\n",
|
|
|
- " continue\n",
|
|
|
- " \n",
|
|
|
- " if time.time() < next_frame:\n",
|
|
|
- " time.sleep(0.001)\n",
|
|
|
- " continue\n",
|
|
|
- " \n",
|
|
|
- " self.count() # count FPS\n",
|
|
|
- " self.detect_position(frame)\n",
|
|
|
- " next_frame += 1.0/self.cfg['max_fps']\n",
|
|
|
- "\n",
|
|
|
- " if self.status.value == 2 and self.positions_list_1 is not None:\n",
|
|
|
- " with open(self.cfg['file_path'], 'a') as f: # save position\n",
|
|
|
- " xy1 = self.xy1_in_m\n",
|
|
|
- " xy2 = self.xy2_in_m\n",
|
|
|
- " f.write(\",\".join([str(x) for x in (self.frame_times[-1], \\\n",
|
|
|
- " round(xy1[0], 4), round(xy1[1], 4), round(xy2[0], 4), round(xy2[1], 4))]) + \"\\n\") \n",
|
|
|
- " \n",
|
|
|
- " # TODO save contours\n",
|
|
|
- " #if not len(self.contour) > 0:\n",
|
|
|
- " # print('No contours')\n",
|
|
|
- " # continue\n",
|
|
|
- " #ctr_in_m = np.array([self.px_to_meters(x, y) for x, y in zip(self.contour[:, 0, 0], self.contour[:, 0, 1])])\n",
|
|
|
- " #data = [\"%.4f:%4f\" % (x[0], x[1]) for x in ctr_in_m]\n",
|
|
|
- " #with open(self.cfg['contour_path'], 'a+') as f: \n",
|
|
|
- " # f.write(\",\".join(data) + \"\\n\") \n",
|
|
|
- " \n",
|
|
|
- " def switch_background(self):\n",
|
|
|
- " self.background = self.bg_dark if self.is_light else self.bg_light \n",
|
|
|
- " self.is_light = not self.is_light\n",
|
|
|
- " \n",
|
|
|
- " def detect_position(self, frame):\n",
|
|
|
- " masked_frame = cv2.bitwise_and(src1=frame, src2=self.mask)\n",
|
|
|
- "\n",
|
|
|
- " # Substracts background from current frame or takes absdiff\n",
|
|
|
- " if 'subtract' in self.cfg:\n",
|
|
|
- " if self.cfg['subtract'] > 0:\n",
|
|
|
- " subject = cv2.subtract(self.background, masked_frame)\n",
|
|
|
- " else:\n",
|
|
|
- " subject = cv2.subtract(self.background, masked_frame)\n",
|
|
|
- " else:\n",
|
|
|
- " subject = cv2.absdiff(masked_frame, self.background)\n",
|
|
|
- "\n",
|
|
|
- " # Converts subject to grey scale\n",
|
|
|
- " subject_gray = cv2.cvtColor(subject, cv2.COLOR_BGR2GRAY)\n",
|
|
|
- "\n",
|
|
|
- " # Applies blur and thresholding to the subject\n",
|
|
|
- " kernel_size = (25,25)\n",
|
|
|
- " frame_blur = cv2.GaussianBlur(subject_gray, kernel_size, 0)\n",
|
|
|
- " th = self.cfg['threshold_light'] if self.is_light else self.cfg['threshold_dark']\n",
|
|
|
- " _, thresh = cv2.threshold(frame_blur, th, 255, cv2.THRESH_BINARY)\n",
|
|
|
- "\n",
|
|
|
- " # Finds contours and selects the contour with the largest area\n",
|
|
|
- " contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)\n",
|
|
|
- "\n",
|
|
|
- " # filter by size\n",
|
|
|
- " blobs = [cnt for cnt in contours if cv2.contourArea(cnt) > self.cfg['min_blob_size']]\n",
|
|
|
- " if len(blobs) == 0: # do nothing if nothing is identified\n",
|
|
|
- " return\n",
|
|
|
- " \n",
|
|
|
- " sizes = [int(cv2.contourArea(blob)) for blob in blobs]\n",
|
|
|
- " blobs = [blobs[i] for i in np.argsort(sizes)][:2] # two largest blobs\n",
|
|
|
- " \n",
|
|
|
- " if len(blobs) == 1: # when two objects are together\n",
|
|
|
- " blobs.append(np.array(blobs[0]))\n",
|
|
|
- " \n",
|
|
|
- " #if np.random.rand() > 0.5: # randomize for testing\n",
|
|
|
- " # blobs[0], blobs[1] = blobs[1], blobs[0]\n",
|
|
|
- " \n",
|
|
|
- " M1 = cv2.moments(blobs[0])\n",
|
|
|
- " M2 = cv2.moments(blobs[1])\n",
|
|
|
- " if M1['m00'] == 0 or M2['m00'] == 0:\n",
|
|
|
- " return\n",
|
|
|
- " \n",
|
|
|
- " xy1 = np.array([M1['m10'] / M1['m00'], M1['m01'] / M1['m00']]) # single pair\n",
|
|
|
- " xy2 = np.array([M2['m10'] / M2['m00'], M2['m01'] / M2['m00']]) # single pair\n",
|
|
|
- "\n",
|
|
|
- " if self.positions_list_1 is None: # first detected coordinates\n",
|
|
|
- " self.positions_list_1 = np.array([xy1]) # array of XY pairs\n",
|
|
|
- " self.positions_list_2 = np.array([xy2]) # array of XY pairs\n",
|
|
|
- "\n",
|
|
|
- " else:\n",
|
|
|
- " idx = 0 if len(self.positions_list_1) < 20 else 1 # append or shift position list, collecting position history\n",
|
|
|
- "\n",
|
|
|
- " # compute distance matrix: current XY1 and XY2 to previous positions\n",
|
|
|
- " dist_array = []\n",
|
|
|
- " for point in (xy1, xy2):\n",
|
|
|
- " for p_list in (self.positions_list_1, self.positions_list_2):\n",
|
|
|
- " distance = np.sqrt( ((p_list[:, 0] - point[0]).mean())**2 + \\\n",
|
|
|
- " ((p_list[:, 1] - point[1]).mean())**2 )\n",
|
|
|
- " dist_array.append(distance)\n",
|
|
|
- "\n",
|
|
|
- " self.dist_array = dist_array\n",
|
|
|
- "\n",
|
|
|
- " if np.argmin(np.array(dist_array)) < 1 or np.argmin(np.array(dist_array)) > 2: # 1 goes to 1, 2 to 2\n",
|
|
|
- " self.positions_list_1 = np.concatenate([self.positions_list_1[idx:], [xy1]])\n",
|
|
|
- " self.positions_list_2 = np.concatenate([self.positions_list_2[idx:], [xy2]])\n",
|
|
|
- " self.contour1, self.contour2 = blobs[0], blobs[1]\n",
|
|
|
- " else: # swap\n",
|
|
|
- " self.positions_list_1 = np.concatenate([self.positions_list_1[idx:], [xy2]])\n",
|
|
|
- " self.positions_list_2 = np.concatenate([self.positions_list_2[idx:], [xy1]])\n",
|
|
|
- " self.contour1, self.contour2 = blobs[1], blobs[0]\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def xy1_in_px(self):\n",
|
|
|
- " return self.positions_list_1[-1].astype('int32') if self.positions_list_1 is not None else None\n",
|
|
|
- "\n",
|
|
|
- " @property\n",
|
|
|
- " def xy2_in_px(self):\n",
|
|
|
- " return self.positions_list_2[-1].astype('int32') if self.positions_list_2 is not None else None\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def xy1_in_m(self):\n",
|
|
|
- " if self.positions_list_1 is None:\n",
|
|
|
- " return None\n",
|
|
|
- " x = (self.cfg['arena_x'] - self.positions_list_1[-1][0]) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " y = (self.cfg['arena_y'] - self.positions_list_1[-1][1]) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
- " return np.array([x, y])\n",
|
|
|
+ " return [self._contour1, self._contour2] if self._contour1 is not None else None\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
- " def xy2_in_m(self):\n",
|
|
|
- " if self.positions_list_2 is None:\n",
|
|
|
- " return None\n",
|
|
|
- " x = (self.cfg['arena_x'] - self.positions_list_2[-1][0]) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " y = (self.cfg['arena_y'] - self.positions_list_2[-1][1]) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
- " return np.array([x, y])\n",
|
|
|
- "\n",
|
|
|
- " # generic interface\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def positions_in_px(self):\n",
|
|
|
- " return np.array([self.xy1_in_px, self.xy2_in_px]) if self.xy1_in_px is not None else None\n",
|
|
|
+ " def speeds(self):\n",
|
|
|
+ " # Not Implemented\n",
|
|
|
+ " return [0, 0] if self.positions_in_px is not None else None\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
- " def positions_in_m(self):\n",
|
|
|
- " return np.array([self.xy1_in_m, self.xy2_in_m]) if self.xy1_in_px is not None else None\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def contours(self): # always in pixels\n",
|
|
|
- " return [self.contour1, self.contour2]\n",
|
|
|
- " \n",
|
|
|
- " def is_inside(self, x, y, r):\n",
|
|
|
- " for pos in self.positions_in_m:\n",
|
|
|
- " if (pos[0] - x)**2 + (pos[1] - y)**2 <= r**2:\n",
|
|
|
- " return True\n",
|
|
|
- " return False"
|
|
|
+ " def hds(self):\n",
|
|
|
+ " # Not Implemented\n",
|
|
|
+ " return [0, 0] if self.positions_in_px is not None else None"
|
|
|
]
|
|
|
},
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 89,
|
|
|
+ "execution_count": 61,
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
|
- "class ZZPositionTracker(FPSTimes):\n",
|
|
|
- "\n",
|
|
|
- " default_cfg = {\n",
|
|
|
- " \"background_light\": \"background_light.png\",\n",
|
|
|
- " \"background_dark\": \"background_dark.png\",\n",
|
|
|
- " \"threshold_light\": 60,\n",
|
|
|
- " \"threshold_dark\": 30,\n",
|
|
|
- " \"min_blob_size\": 100,\n",
|
|
|
- " \"subtract\": 1,\n",
|
|
|
- " \"arena_x\": 522,\n",
|
|
|
- " \"arena_y\": 372,\n",
|
|
|
- " \"arena_radius\": 330,\n",
|
|
|
- " \"floor_radius\": 287,\n",
|
|
|
- " \"max_fps\": 50,\n",
|
|
|
- " \"file_path\": \"positions.csv\",\n",
|
|
|
- " \"contour_path\": \"contours.csv\",\n",
|
|
|
- " \"floor_r_in_meters\": 0.46,\n",
|
|
|
- " \"angle_compensation\": -90,\n",
|
|
|
- " \"flip_x\": True,\n",
|
|
|
- " \"flip_y\": False\n",
|
|
|
- " }\n",
|
|
|
+ "class PositionTrackerSingle(PositionTrackerBase):\n",
|
|
|
" \n",
|
|
|
" def __init__(self, status, video_stream, cfg):\n",
|
|
|
- " super(PositionTracker, self).__init__()\n",
|
|
|
+ " super(PositionTrackerSingle, self).__init__(status, video_stream, cfg)\n",
|
|
|
+ " \n",
|
|
|
+ " # an array of [[t1, X1, Y1, speed1, HD1], [t2, X2, Y2, speed2, HD2], ...] of 'history_duration' length\n",
|
|
|
+ " # X, Y - in pixels, Speed - in m/s, head direction (HD) - in rad\n",
|
|
|
+ " self._positions_list = None \n",
|
|
|
+ " self._contour = None\n",
|
|
|
+ " self._lr = None # linear regression of the contour\n",
|
|
|
+ " \n",
|
|
|
+ " width = 50 # 50 points ~= 1 sec with at 50Hz - to smooth the trajectory\n",
|
|
|
+ " self.kernel = signal.gaussian(width, std=(width) / 7.2)\n",
|
|
|
" \n",
|
|
|
- " self.status = status\n",
|
|
|
- " self.cfg = cfg\n",
|
|
|
- " self.video_stream = video_stream\n",
|
|
|
- " self.bg_light = cv2.imread(cfg['background_light'], 1)\n",
|
|
|
- " self.bg_dark = cv2.imread(cfg['background_dark'], 1)\n",
|
|
|
- " self.background = self.bg_light # light by default\n",
|
|
|
- " self.is_light = True\n",
|
|
|
- " self._x, self._y = None, None\n",
|
|
|
- " self.pixel_size = cfg['floor_r_in_meters'] / float(cfg['floor_radius'])\n",
|
|
|
- " self.contour = []\n",
|
|
|
- " self.lr = None # linear regression of the contour\n",
|
|
|
- " self.stopped = False\n",
|
|
|
- "\n",
|
|
|
- " self.mask = np.zeros(shape=self.background.shape, dtype=\"uint8\")\n",
|
|
|
- " cv2.circle(self.mask, (cfg['arena_x'], cfg['arena_y']), cfg['arena_radius'], (255,255,255), -1)\n",
|
|
|
- "\n",
|
|
|
" with open(cfg['file_path'], 'w') as f:\n",
|
|
|
" f.write(\"time,x,y\\n\")\n",
|
|
|
" with open(cfg['contour_path'], 'w') as f:\n",
|
|
|
" f.write(\"x:y,...\\n\")\n",
|
|
|
- "\n",
|
|
|
- " def reload_background(self):\n",
|
|
|
- " self.bg_light = cv2.imread(self.cfg['background_light'], 1)\n",
|
|
|
- " self.bg_dark = cv2.imread(self.cfg['background_dark'], 1)\n",
|
|
|
- " self.background = self.bg_light if self.is_light else self.bg_dark\n",
|
|
|
- " print('Position tracker - background reloaded')\n",
|
|
|
- " \n",
|
|
|
- " def px_to_meters(self, x, y):\n",
|
|
|
- " x_m = float(self.cfg['arena_x'] - x) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " y_m = float(self.cfg['arena_y'] - y) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
- " return x_m, y_m\n",
|
|
|
- "\n",
|
|
|
- " def meters_to_px(self, x, y):\n",
|
|
|
- " x_m = self.cfg['arena_x'] - (x / self.pixel_size) * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " y_m = self.cfg['arena_y'] - (y / self.pixel_size) * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
- " return int(x_m), int(y_m)\n",
|
|
|
- "\n",
|
|
|
- " def correct_angle(self, phi):\n",
|
|
|
- " return (2*np.pi - phi) + np.deg2rad(self.cfg['angle_compensation'])\n",
|
|
|
- " \n",
|
|
|
- " def start(self):\n",
|
|
|
- " self._th = threading.Thread(target=self.update, args=())\n",
|
|
|
- " self._th.start()\n",
|
|
|
- " \n",
|
|
|
- " def stop(self):\n",
|
|
|
- " self.stopped = True\n",
|
|
|
- " self._th.join()\n",
|
|
|
- " print('Position tracker stopped')\n",
|
|
|
- " \n",
|
|
|
- " def update(self):\n",
|
|
|
- " next_frame = time.time() + 1.0/self.cfg['max_fps']\n",
|
|
|
- " \n",
|
|
|
- " while not self.stopped:\n",
|
|
|
- " frame = self.video_stream.read()\n",
|
|
|
- " if frame is None:\n",
|
|
|
- " time.sleep(0.05)\n",
|
|
|
- " continue\n",
|
|
|
- " \n",
|
|
|
- " if time.time() < next_frame:\n",
|
|
|
- " time.sleep(0.001)\n",
|
|
|
- " continue\n",
|
|
|
- " \n",
|
|
|
- " self.count() # count FPS\n",
|
|
|
- " self.detect_position(frame)\n",
|
|
|
- " next_frame += 1.0/self.cfg['max_fps']\n",
|
|
|
- "\n",
|
|
|
- " if self.status.value == 2 and self._x is not None:\n",
|
|
|
- " # save position\n",
|
|
|
- " with open(self.cfg['file_path'], 'a') as f:\n",
|
|
|
- " f.write(\",\".join([str(x) for x in (self.frame_times[-1], \\\n",
|
|
|
- " round(self.x_in_m, 4), round(self.y_in_m, 4))]) + \"\\n\") \n",
|
|
|
- " \n",
|
|
|
- " # save contours\n",
|
|
|
- " if not len(self.contour) > 0:\n",
|
|
|
- " print('No contours')\n",
|
|
|
- " continue\n",
|
|
|
- " ctr_in_m = np.array([self.px_to_meters(x, y) for x, y in zip(self.contour[:, 0, 0], self.contour[:, 0, 1])])\n",
|
|
|
- " data = [\"%.4f:%4f\" % (x[0], x[1]) for x in ctr_in_m]\n",
|
|
|
- " with open(self.cfg['contour_path'], 'a+') as f: \n",
|
|
|
- " f.write(\",\".join(data) + \"\\n\") \n",
|
|
|
- " \n",
|
|
|
- " def switch_background(self):\n",
|
|
|
- " self.background = self.bg_dark if self.is_light else self.bg_light \n",
|
|
|
- " self.is_light = not self.is_light\n",
|
|
|
- " \n",
|
|
|
+ " \n",
|
|
|
" def detect_position(self, frame):\n",
|
|
|
" masked_frame = cv2.bitwise_and(src1=frame, src2=self.mask)\n",
|
|
|
"\n",
|
|
@@ -769,61 +458,100 @@
|
|
|
" if (M['m00'] == 0):\n",
|
|
|
" return\n",
|
|
|
"\n",
|
|
|
- " self._x = M['m10'] / M['m00']\n",
|
|
|
- " self._y = M['m01'] / M['m00']\n",
|
|
|
- " self.contour = contour # in pixels\n",
|
|
|
- " \n",
|
|
|
- " ctr_in_px = np.array([x for x in zip(contour[:, 0, 0], contour[:, 0, 1])])\n",
|
|
|
- " if ctr_in_px[:, 0].max() - ctr_in_px[:, 0].min() > ctr_in_px[:, 1].max() - ctr_in_px[:, 1].min():\n",
|
|
|
- " slope, intercept, r_value, p_value, std_err = stats.linregress(ctr_in_px[:, 0], ctr_in_px[:, 1])\n",
|
|
|
- " self.lr = {'slope': slope, 'intercept': intercept, 'r_value': r_value, 'p_value': p_value, 'std_err': std_err}\n",
|
|
|
+ " x, y = M['m10'] / M['m00'], M['m01'] / M['m00']\n",
|
|
|
+ " if self._positions_list is None:\n",
|
|
|
+ " self._positions_list = np.array([[time.time(), x, y, 0, 0]])\n",
|
|
|
" else:\n",
|
|
|
- " slope, intercept, r_value, p_value, std_err = stats.linregress(ctr_in_px[:, 1], ctr_in_px[:, 0])\n",
|
|
|
- " self.lr = {'slope': 1 - slope, 'intercept': intercept, 'r_value': r_value, 'p_value': p_value, 'std_err': std_err}\n",
|
|
|
+ " t1 = time.time() - self.cfg['history_duration']\n",
|
|
|
+ " idx = np.argmin(np.abs(self._positions_list[:, 0] - t1))\n",
|
|
|
+ " last_hd = self._positions_list[-1][4]\n",
|
|
|
+ " self._positions_list = np.concatenate([self._positions_list[idx:], [np.array([time.time(), x, y, 0, last_hd])]])\n",
|
|
|
+ "\n",
|
|
|
+ " # speed\n",
|
|
|
+ " if len(self._positions_list) > len(self.kernel):\n",
|
|
|
+ " x = (-self._positions_list[:, 1] + self.cfg['arena_x']) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
+ " y = (-self._positions_list[:, 2] + self.cfg['arena_y']) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
" \n",
|
|
|
- " # local single-agent interface\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def x_in_px(self):\n",
|
|
|
- " return int(self._x) if self._x is not None else None\n",
|
|
|
+ " # to avoid boundary effects\n",
|
|
|
+ " x = np.concatenate([np.ones(int(len(self.kernel)/2) - 1) * x[0], x, np.ones(int(len(self.kernel)/2)) * x[-1]])\n",
|
|
|
+ " y = np.concatenate([np.ones(int(len(self.kernel)/2) - 1) * y[0], y, np.ones(int(len(self.kernel)/2)) * y[-1]])\n",
|
|
|
+ " \n",
|
|
|
+ " x_smooth = np.convolve(x, self.kernel, 'valid') / self.kernel.sum() # valid mode to avoid boundary effects\n",
|
|
|
+ " y_smooth = np.convolve(y, self.kernel, 'valid') / self.kernel.sum()\n",
|
|
|
"\n",
|
|
|
- " @property\n",
|
|
|
- " def y_in_px(self):\n",
|
|
|
- " return int(self._y) if self._y is not None else None\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def x_in_m(self):\n",
|
|
|
- " if self._x is None:\n",
|
|
|
- " return None\n",
|
|
|
- " return (self.cfg['arena_x'] - self._x) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
- " \n",
|
|
|
- " @property\n",
|
|
|
- " def y_in_m(self):\n",
|
|
|
- " if self._y is None:\n",
|
|
|
- " return None\n",
|
|
|
- " return (self.cfg['arena_y'] - self._y) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1) \n",
|
|
|
+ " dx = np.sqrt(np.square(np.diff(x_smooth)) + np.square(np.diff(y_smooth)))\n",
|
|
|
+ " dt = np.diff(self._positions_list[:, 0])\n",
|
|
|
+ " speed = np.concatenate([dx/dt, [dx[-1]/dt[-1]]])\n",
|
|
|
+ " self._positions_list[:, 3] = speed # in m/s\n",
|
|
|
+ " \n",
|
|
|
+ " # head direction\n",
|
|
|
+ " recent_traj = self._positions_list[self._positions_list[:, 0] > time.time() - 0.5]\n",
|
|
|
+ " avg_speed = recent_traj[:, 3].mean()\n",
|
|
|
+ " if avg_speed > self.cfg['hd_update_speed']: # if animal runs basically\n",
|
|
|
+ " x, y = recent_traj[0][1], recent_traj[0][2]\n",
|
|
|
+ " vectors = [np.array([a[1], a[2]]) - np.array([x, y]) for a in recent_traj[1:]]\n",
|
|
|
+ " avg_direction = np.array(vectors).sum(axis=0) / len(vectors)\n",
|
|
|
+ "\n",
|
|
|
+ " avg_angle = -np.arctan2(avg_direction[1], avg_direction[0])\n",
|
|
|
+ " self._positions_list[-1][4] = avg_angle # in radians\n",
|
|
|
+ " \n",
|
|
|
+ " self._contour = contour # in pixels\n",
|
|
|
+ " \n",
|
|
|
+ " #ctr_in_px = np.array([x for x in zip(contour[:, 0, 0], contour[:, 0, 1])])\n",
|
|
|
+ " #if ctr_in_px[:, 0].max() - ctr_in_px[:, 0].min() > ctr_in_px[:, 1].max() - ctr_in_px[:, 1].min():\n",
|
|
|
+ " # slope, intercept, r_value, p_value, std_err = stats.linregress(ctr_in_px[:, 0], ctr_in_px[:, 1])\n",
|
|
|
+ " # self.lr = {'slope': slope, 'intercept': intercept, 'r_value': r_value, 'p_value': p_value, 'std_err': std_err}\n",
|
|
|
+ " #else:\n",
|
|
|
+ " # slope, intercept, r_value, p_value, std_err = stats.linregress(ctr_in_px[:, 1], ctr_in_px[:, 0])\n",
|
|
|
+ " # self.lr = {'slope': 1 - slope, 'intercept': intercept, 'r_value': r_value, 'p_value': p_value, 'std_err': std_err}\n",
|
|
|
+ "\n",
|
|
|
+ " def save_position(self):\n",
|
|
|
+ " if self.positions_in_px is None:\n",
|
|
|
+ " return\n",
|
|
|
+ " with open(self.cfg['file_path'], 'a') as f:\n",
|
|
|
+ " f.write(\",\".join([str(x) for x in (self.frame_times[-1], \\\n",
|
|
|
+ " round(self.positions_in_m[0][0], 4), round(self.positions_in_m[0][1], 4))]) + \"\\n\") \n",
|
|
|
" \n",
|
|
|
- " # generic interface\n",
|
|
|
+ " def save_contours(self):\n",
|
|
|
+ " if self.contours is None:\n",
|
|
|
+ " return\n",
|
|
|
+ " ctr_in_m = np.array([self.px_to_meters(x, y) for x, y in zip(self._contour[:, 0, 0], self._contour[:, 0, 1])])\n",
|
|
|
+ " data = [\"%.4f:%.4f\" % (x[0], x[1]) for x in ctr_in_m]\n",
|
|
|
+ " with open(self.cfg['contour_path'], 'a+') as f: \n",
|
|
|
+ " f.write(\",\".join(data) + \"\\n\")\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def positions_in_px(self):\n",
|
|
|
- " return [np.array([self.x_in_px, self.y_in_px])] if self.x_in_px is not None else None\n",
|
|
|
+ " return [self._positions_list[-1][1:].astype('int32')] if self._positions_list is not None else None\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def positions_in_m(self):\n",
|
|
|
- " return [np.array([self.x_in_m, self.y_in_m])] if self.x_in_m is not None else None\n",
|
|
|
+ " if self._positions_list is None:\n",
|
|
|
+ " return None\n",
|
|
|
+ " x = (self.cfg['arena_x'] - self._positions_list[-1][1]) * self.pixel_size * (-1 if self.cfg['flip_x'] else 1)\n",
|
|
|
+ " y = (self.cfg['arena_y'] - self._positions_list[-1][2]) * self.pixel_size * (-1 if self.cfg['flip_y'] else 1)\n",
|
|
|
+ " return [np.array([x, y])]\n",
|
|
|
" \n",
|
|
|
" @property\n",
|
|
|
" def contours(self):\n",
|
|
|
- " return [self.contour]\n",
|
|
|
+ " return [self._contour] if self._contour is not None else None\n",
|
|
|
" \n",
|
|
|
- " def is_inside(self, x, y, r):\n",
|
|
|
- " for pos in self.positions_in_m:\n",
|
|
|
- " if (pos[0] - x)**2 + (pos[1] - y)**2 <= r**2:\n",
|
|
|
- " return True\n",
|
|
|
- " return False"
|
|
|
+ " @property\n",
|
|
|
+ " def speeds(self):\n",
|
|
|
+ " return [self._positions_list[-1][3]] if self._positions_list is not None else None\n",
|
|
|
+ " \n",
|
|
|
+ " @property\n",
|
|
|
+ " def hds(self):\n",
|
|
|
+ " return [np.degrees(self._positions_list[-1][4])] if self._positions_list is not None else None"
|
|
|
]
|
|
|
},
|
|
|
+ {
|
|
|
+ "cell_type": "code",
|
|
|
+ "execution_count": null,
|
|
|
+ "metadata": {},
|
|
|
+ "outputs": [],
|
|
|
+ "source": []
|
|
|
+ },
|
|
|
{
|
|
|
"cell_type": "markdown",
|
|
|
"metadata": {},
|
|
@@ -833,11 +561,11 @@
|
|
|
},
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 84,
|
|
|
+ "execution_count": 48,
|
|
|
"metadata": {},
|
|
|
"outputs": [],
|
|
|
"source": [
|
|
|
- "with open(os.path.join('..', 'profiles', 'andrey_hippoSIT_008229_multiple.json')) as json_file:\n",
|
|
|
+ "with open(os.path.join('..', 'profiles', 'default.json')) as json_file:\n",
|
|
|
" cfg = json.load(json_file)\n",
|
|
|
"\n",
|
|
|
"pt_cfg = cfg['position']\n",
|
|
@@ -847,7 +575,7 @@
|
|
|
},
|
|
|
{
|
|
|
"cell_type": "code",
|
|
|
- "execution_count": 85,
|
|
|
+ "execution_count": 49,
|
|
|
"metadata": {
|
|
|
"scrolled": true
|
|
|
},
|
|
@@ -857,129 +585,6 @@
|
|
|
"output_type": "stream",
|
|
|
"text": [
|
|
|
"Webcam stream 1024.0:768.0 at 30.00 FPS started\n",
|
|
|
- "[[649.13703317 450.80120622]\n",
|
|
|
- " [649.13703317 450.80120622]\n",
|
|
|
- " [649.17979025 450.82246944]\n",
|
|
|
- " [649.12542687 450.80997858]\n",
|
|
|
- " [649.12542687 450.80997858]\n",
|
|
|
- " [649.11041485 450.78497244]\n",
|
|
|
- " [649.18280506 450.7834088 ]\n",
|
|
|
- " [649.18280506 450.7834088 ]\n",
|
|
|
- " [649.17481868 450.77905425]\n",
|
|
|
- " [649.17481868 450.77905425]\n",
|
|
|
- " [649.17921957 450.76529251]\n",
|
|
|
- " [649.17921957 450.76529251]\n",
|
|
|
- " [649.16588365 450.80766777]\n",
|
|
|
- " [649.16588365 450.80766777]\n",
|
|
|
- " [649.16214178 450.83542174]\n",
|
|
|
- " [649.21385402 450.77969549]\n",
|
|
|
- " [649.21385402 450.77969549]\n",
|
|
|
- " [649.14980297 450.81664349]\n",
|
|
|
- " [649.14980297 450.81664349]\n",
|
|
|
- " [649.14715447 450.79343786]]\n",
|
|
|
- "[[361.16319846 244.74470135]\n",
|
|
|
- " [361.16319846 244.74470135]\n",
|
|
|
- " [361.2920982 244.7079018 ]\n",
|
|
|
- " [361.03257576 244.89924242]\n",
|
|
|
- " [361.03257576 244.89924242]\n",
|
|
|
- " [361.04383457 244.82008767]\n",
|
|
|
- " [361.15450399 244.81071836]\n",
|
|
|
- " [361.15450399 244.81071836]\n",
|
|
|
- " [360.9655106 244.76840077]\n",
|
|
|
- " [360.9655106 244.76840077]\n",
|
|
|
- " [361.09638554 244.80569899]\n",
|
|
|
- " [361.09638554 244.80569899]\n",
|
|
|
- " [361.01837121 244.85340909]\n",
|
|
|
- " [361.01837121 244.85340909]\n",
|
|
|
- " [361.07641259 244.85381115]\n",
|
|
|
- " [361.00415722 244.8622449 ]\n",
|
|
|
- " [361.00415722 244.8622449 ]\n",
|
|
|
- " [361.31685824 244.70708812]\n",
|
|
|
- " [361.31685824 244.70708812]\n",
|
|
|
- " [361.24548251 244.69184929]]\n",
|
|
|
- "[0.020170695587446313, 354.12117522709224, 354.08654128820183, 0.1781380511142404]\n",
|
|
|
- "[[649.17870015 450.76584118]\n",
|
|
|
- " [649.18850695 450.80451346]\n",
|
|
|
- " [649.2322352 450.7480681 ]\n",
|
|
|
- " [649.2322352 450.7480681 ]\n",
|
|
|
- " [649.2153238 450.78049064]\n",
|
|
|
- " [649.2153238 450.78049064]\n",
|
|
|
- " [649.14344739 450.78933892]\n",
|
|
|
- " [649.16678301 450.74037231]\n",
|
|
|
- " [649.16678301 450.74037231]\n",
|
|
|
- " [649.19526799 450.80891757]\n",
|
|
|
- " [649.19526799 450.80891757]\n",
|
|
|
- " [649.15023884 450.79593382]\n",
|
|
|
- " [649.15023884 450.79593382]\n",
|
|
|
- " [649.18931333 450.7689982 ]\n",
|
|
|
- " [649.21511018 450.75743267]\n",
|
|
|
- " [649.21511018 450.75743267]\n",
|
|
|
- " [649.23117623 450.73794988]\n",
|
|
|
- " [649.23117623 450.73794988]\n",
|
|
|
- " [649.18650378 450.7646306 ]\n",
|
|
|
- " [649.20895696 450.79417566]]\n",
|
|
|
- "[[361.0732792 244.78982985]\n",
|
|
|
- " [361.08523592 244.8053653 ]\n",
|
|
|
- " [361.19641826 244.70595032]\n",
|
|
|
- " [361.19641826 244.70595032]\n",
|
|
|
- " [361.10225303 244.77488927]\n",
|
|
|
- " [361.10225303 244.77488927]\n",
|
|
|
- " [361.09928668 244.7312512 ]\n",
|
|
|
- " [361.15027006 244.76948302]\n",
|
|
|
- " [361.15027006 244.76948302]\n",
|
|
|
- " [360.97819851 244.81564353]\n",
|
|
|
- " [360.97819851 244.81564353]\n",
|
|
|
- " [361.00902978 244.83938521]\n",
|
|
|
- " [361.00902978 244.83938521]\n",
|
|
|
- " [360.99651365 244.75305055]\n",
|
|
|
- " [361.08419048 244.82857143]\n",
|
|
|
- " [361.08419048 244.82857143]\n",
|
|
|
- " [361.33256528 244.6858679 ]\n",
|
|
|
- " [361.33256528 244.6858679 ]\n",
|
|
|
- " [361.12078119 244.85381115]\n",
|
|
|
- " [361.25416906 244.71765382]]\n",
|
|
|
- "[0.028602276327075016, 354.1820666577614, 354.0717304601498, 0.15845490244796706]\n",
|
|
|
- "[[649.19886927 450.77035612]\n",
|
|
|
- " [649.20092996 450.7617553 ]\n",
|
|
|
- " [649.20092996 450.7617553 ]\n",
|
|
|
- " [649.16084933 450.76340896]\n",
|
|
|
- " [649.16306118 450.81548034]\n",
|
|
|
- " [649.16306118 450.81548034]\n",
|
|
|
- " [649.26006878 450.73188786]\n",
|
|
|
- " [649.19757618 450.74177009]\n",
|
|
|
- " [649.19757618 450.74177009]\n",
|
|
|
- " [649.22239711 450.73108313]\n",
|
|
|
- " [649.22239711 450.73108313]\n",
|
|
|
- " [649.19528973 450.76238778]\n",
|
|
|
- " [649.19528973 450.76238778]\n",
|
|
|
- " [649.17570246 450.79456686]\n",
|
|
|
- " [649.24678199 450.74552973]\n",
|
|
|
- " [649.24678199 450.74552973]\n",
|
|
|
- " [649.23368298 450.75635198]\n",
|
|
|
- " [649.23368298 450.75635198]\n",
|
|
|
- " [649.22400652 450.7307849 ]\n",
|
|
|
- " [649.20655146 450.74789085]]\n",
|
|
|
- "[[360.9847343 244.80888889]\n",
|
|
|
- " [361.17565455 244.70672138]\n",
|
|
|
- " [361.17565455 244.70672138]\n",
|
|
|
- " [361.36436378 244.59992194]\n",
|
|
|
- " [361.21778549 244.72453704]\n",
|
|
|
- " [361.21778549 244.72453704]\n",
|
|
|
- " [361.24727414 244.61487539]\n",
|
|
|
- " [361.14763015 244.7039627 ]\n",
|
|
|
- " [361.14763015 244.7039627 ]\n",
|
|
|
- " [361.33064825 244.73571155]\n",
|
|
|
- " [361.33064825 244.73571155]\n",
|
|
|
- " [361.14544753 244.73206019]\n",
|
|
|
- " [361.14544753 244.73206019]\n",
|
|
|
- " [361.18310955 244.66686126]\n",
|
|
|
- " [361.14240631 244.69171598]\n",
|
|
|
- " [361.14240631 244.69171598]\n",
|
|
|
- " [361.2206655 244.70052539]\n",
|
|
|
- " [361.2206655 244.70052539]\n",
|
|
|
- " [361.09668026 244.67171423]\n",
|
|
|
- " [361.32717475 244.71920708]]\n",
|
|
|
- "[0.011617968545925627, 354.13427120702806, 354.01600877300143, 0.14652917487666503]\n",
|
|
|
"Camera released\n",
|
|
|
"Position tracker stopped\n"
|
|
|
]
|
|
@@ -997,7 +602,8 @@
|
|
|
"vs.start() # stream runs in a separate thread\n",
|
|
|
"\n",
|
|
|
"# init controller\n",
|
|
|
- "pt = DoublePositionTracker(status, vs, pt_cfg)\n",
|
|
|
+ "#pt = DoublePositionTracker(status, vs, pt_cfg)\n",
|
|
|
+ "pt = PositionTrackerSingleHD(status, vs, pt_cfg)\n",
|
|
|
"pt.start()\n",
|
|
|
"kernel_size = (25,25)\n",
|
|
|
"\n",
|
|
@@ -1016,18 +622,18 @@
|
|
|
" cv2.putText(masked_frame, 'Position: %.2f FPS' % pt.get_avg_fps(), \n",
|
|
|
" (10, 80), cv2.FONT_HERSHEY_DUPLEX, .5, (255, 255, 255)) \n",
|
|
|
" \n",
|
|
|
- " if pt.positions_list_1 is not None:\n",
|
|
|
+ " if pt.positions_in_px is not None:\n",
|
|
|
" color1 = (127, 255, 0)\n",
|
|
|
- " color2 = (0, 0, 255)\n",
|
|
|
- " cv2.circle(masked_frame, (pt.xy1_in_px[0], pt.xy1_in_px[1]), 2, color1, -1)\n",
|
|
|
- " cv2.circle(masked_frame, (pt.xy2_in_px[0], pt.xy2_in_px[1]), 2, color2, -1)\n",
|
|
|
+ " #color2 = (0, 0, 255)\n",
|
|
|
+ " cv2.circle(masked_frame, (pt.positions_in_px[0][0], pt.positions_in_px[0][1]), 2, color1, -1)\n",
|
|
|
+ " #cv2.circle(masked_frame, (pt.xy2_in_px[0], pt.xy2_in_px[1]), 2, color2, -1)\n",
|
|
|
" \n",
|
|
|
- " cv2.drawContours(masked_frame, [pt.contour1], 0, color1, 1, cv2.LINE_AA)\n",
|
|
|
- " cv2.drawContours(masked_frame, [pt.contour2], 0, color2, 1, cv2.LINE_AA)\n",
|
|
|
+ " cv2.drawContours(masked_frame, [pt.contours[0]], 0, color1, 1, cv2.LINE_AA)\n",
|
|
|
+ " #cv2.drawContours(masked_frame, [pt.contour2], 0, color2, 1, cv2.LINE_AA)\n",
|
|
|
" \n",
|
|
|
- " cv2.putText(masked_frame, 'Animal1: %.3f %.3f %d' % (pt.xy1_in_m[0], pt.xy1_in_m[1], cv2.contourArea(pt.contour1)), \\\n",
|
|
|
- " (10, 40), cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255))\n",
|
|
|
- " cv2.putText(masked_frame, 'Animal2: %.3f %.3f %d' % (pt.xy2_in_m[0], pt.xy2_in_m[1], cv2.contourArea(pt.contour2)), \\\n",
|
|
|
+ " cv2.putText(masked_frame, 'Animal1: %.3f %.3f %d' % (pt.positions_in_m[0][0], pt.positions_in_m[0][1], \\\n",
|
|
|
+ " cv2.contourArea(pt.contours[0])), (10, 40), cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255))\n",
|
|
|
+ " cv2.putText(masked_frame, 'History: %d' % (len(pt._positions_list)), \\\n",
|
|
|
" (10, 60), cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255))\n",
|
|
|
" \n",
|
|
|
" # regression line\n",
|