Commit 0d9fc5e5 authored by lucas_miranda's avatar lucas_miranda
Browse files

Implementation of elliptic recognition (for videos with tilted cameras)

parent de772f44
......@@ -21,7 +21,6 @@ tensorflow-probability = "*"
tensorflow = "*"
tqdm = "*"
umap-learn = "*"
scikit-image = "*"
[dev-packages]
coverage = "*"
......
{
"_meta": {
"hash": {
"sha256": "2a6fb7ab5adc75487d0fc88fca2bd191dc94e1d1a2c1c119328f2101884f7644"
"sha256": "876ceeb28b81f54c5a6cee38c258395391e3eb58d6e7c3f4844d99896949cae2"
},
"pipfile-spec": 6,
"requires": {
......@@ -221,13 +221,6 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"imageio": {
"hashes": [
"sha256:3604d751f03002e8e0e7650aa71d8d9148144a87daf17cb1f3228e80747f2e6b",
"sha256:52ddbaeca2dccf53ba2d6dec5676ca7bc3b2403ef8b37f7da78b7654bb3e10f0"
],
"version": "==2.9.0"
},
"joblib": {
"hashes": [
"sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f",
......@@ -313,34 +306,34 @@
},
"matplotlib": {
"hashes": [
"sha256:09225edca87a79815822eb7d3be63a83ebd4d9d98d5aa3a15a94f4eee2435954",
"sha256:0caa687fce6174fef9b27d45f8cc57cbc572e04e98c81db8e628b12b563d59a2",
"sha256:27c9393fada62bd0ad7c730562a0fecbd3d5aaa8d9ed80ba7d3ebb8abc4f0453",
"sha256:2c2c5041608cb75c39cbd0ed05256f8a563e144234a524c59d091abbfa7a868f",
"sha256:2d31aff0c8184b05006ad756b9a4dc2a0805e94d28f3abc3187e881b6673b302",
"sha256:3a4c3e9be63adf8e9b305aa58fb3ec40ecc61fd0f8fd3328ce55bc30e7a2aeb0",
"sha256:5111d6d47a0f5b8f3e10af7a79d5e7eb7e73a22825391834734274c4f312a8a0",
"sha256:5ed3d3342698c2b1f3651f8ea6c099b0f196d16ee00e33dc3a6fee8cb01d530a",
"sha256:6ffd2d80d76df2e5f9f0c0140b5af97e3b87dd29852dcdb103ec177d853ec06b",
"sha256:746897fbd72bd462b888c74ed35d812ca76006b04f717cd44698cdfc99aca70d",
"sha256:756ee498b9ba35460e4cbbd73f09018e906daa8537fff61da5b5bf8d5e9de5c7",
"sha256:7ad44f2c74c50567c694ee91c6fa16d67e7c8af6f22c656b80469ad927688457",
"sha256:83e6c895d93fdf93eeff1a21ee96778ba65ef258e5d284160f7c628fee40c38f",
"sha256:9b03722c89a43a61d4d148acfc89ec5bb54cd0fd1539df25b10eb9c5fa6c393a",
"sha256:a4fe54eab2c7129add75154823e6543b10261f9b65b2abe692d68743a4999f8c",
"sha256:b1b60c6476c4cfe9e5cf8ab0d3127476fd3d5f05de0f343a452badaad0e4bdec",
"sha256:b26c472847911f5a7eb49e1c888c31c77c4ddf8023c1545e0e8e0367ba74fb15",
"sha256:b2a5e1f637a92bb6f3526cc54cc8af0401112e81ce5cba6368a1b7908f9e18bc",
"sha256:b7b09c61a91b742cb5460b72efd1fe26ef83c1c704f666e0af0df156b046aada",
"sha256:b8ba2a1dbb4660cb469fe8e1febb5119506059e675180c51396e1723ff9b79d9",
"sha256:c092fc4673260b1446b8578015321081d5db73b94533fe4bf9b69f44e948d174",
"sha256:c586ac1d64432f92857c3cf4478cfb0ece1ae18b740593f8a39f2f0b27c7fda5",
"sha256:d082f77b4ed876ae94a9373f0db96bf8768a7cca6c58fc3038f94e30ffde1880",
"sha256:e71cdd402047e657c1662073e9361106c6981e9621ab8c249388dfc3ec1de07b",
"sha256:eb6b6700ea454bb88333d98601e74928e06f9669c1ea231b4c4c666c1d7701b4"
"sha256:1de0bb6cbfe460725f0e97b88daa8643bcf9571c18ba90bb8e41432aaeca91d6",
"sha256:1e850163579a8936eede29fad41e202b25923a0a8d5ffd08ce50fc0a97dcdc93",
"sha256:215e2a30a2090221a9481db58b770ce56b8ef46f13224ae33afe221b14b24dc1",
"sha256:348e6032f666ffd151b323342f9278b16b95d4a75dfacae84a11d2829a7816ae",
"sha256:3d2eb9c1cc254d0ffa90bc96fde4b6005d09c2228f99dfd493a4219c1af99644",
"sha256:3e477db76c22929e4c6876c44f88d790aacdf3c3f8f3a90cb1975c0bf37825b0",
"sha256:451cc89cb33d6652c509fc6b588dc51c41d7246afdcc29b8624e256b7663ed1f",
"sha256:46b1a60a04e6d884f0250d5cc8dc7bd21a9a96c584a7acdaab44698a44710bab",
"sha256:5f571b92a536206f7958f7cb2d367ff6c9a1fa8229dc35020006e4cdd1ca0acd",
"sha256:672960dd114e342b7c610bf32fb99d14227f29919894388b41553217457ba7ef",
"sha256:7310e353a4a35477c7f032409966920197d7df3e757c7624fd842f3eeb307d3d",
"sha256:746a1df55749629e26af7f977ea426817ca9370ad1569436608dc48d1069b87c",
"sha256:7c155437ae4fd366e2700e2716564d1787700687443de46bcb895fe0f84b761d",
"sha256:9265ae0fb35e29f9b8cc86c2ab0a2e3dcddc4dd9de4b85bf26c0f63fe5c1c2ca",
"sha256:94bdd1d55c20e764d8aea9d471d2ae7a7b2c84445e0fa463f02e20f9730783e1",
"sha256:9a79e5dd7bb797aa611048f5b70588b23c5be05b63eefd8a0d152ac77c4243db",
"sha256:a17f0a10604fac7627ec82820439e7db611722e80c408a726cd00d8c974c2fb3",
"sha256:a1acb72f095f1d58ecc2538ed1b8bca0b57df313b13db36ed34b8cdf1868e674",
"sha256:aa49571d8030ad0b9ac39708ee77bd2a22f87815e12bdee52ecaffece9313ed8",
"sha256:c24c05f645aef776e8b8931cb81e0f1632d229b42b6d216e30836e2e145a2b40",
"sha256:cf3a7e54eff792f0815dbbe9b85df2f13d739289c93d346925554f71d484be78",
"sha256:d738acfdfb65da34c91acbdb56abed46803db39af259b7f194dc96920360dbe4",
"sha256:e15fa23d844d54e7b3b7243afd53b7567ee71c721f592deb0727ee85e668f96a",
"sha256:ed4a9e6dcacba56b17a0a9ac22ae2c72a35b7f0ef0693aa68574f0b2df607a89",
"sha256:f44149a0ef5b4991aaef12a93b8e8d66d6412e762745fea1faa61d98524e0ba9"
],
"index": "pypi",
"version": "==3.3.3"
"version": "==3.3.4"
},
"networkx": {
"hashes": [
......@@ -668,42 +661,6 @@
],
"version": "==2020.5"
},
"pywavelets": {
"hashes": [
"sha256:076ca8907001fdfe4205484f719d12b4a0262dfe6652fa1cfc3c5c362d14dc84",
"sha256:18a51b3f9416a2ae6e9a35c4af32cf520dd7895f2b69714f4aa2f4342fca47f9",
"sha256:1a64b40f6acb4ffbaccce0545d7fc641744f95351f62e4c6aaa40549326008c9",
"sha256:2b634a54241c190ee989a4af87669d377b37c91bcc9cf0efe33c10ff847f7841",
"sha256:2f7429eeb5bf9c7068002d0d7f094ed654c77a70ce5e6198737fd68ab85f8311",
"sha256:35959c041ec014648575085a97b498eafbbaa824f86f6e4a59bfdef8a3fe6308",
"sha256:39c74740718e420d38c78ca4498568fa57976d78d5096277358e0fa9629a7aea",
"sha256:411e17ca6ed8cf5e18a7ca5ee06a91c25800cc6c58c77986202abf98d749273a",
"sha256:55e39ec848ceec13c9fa1598253ae9dd5c31d09dfd48059462860d2b908fb224",
"sha256:6162dc0ae04669ea04b4b51420777b9ea2d30b0a9d02901b2a3b4d61d159c2e9",
"sha256:68b5c33741d26c827074b3d8f0251de1c3019bb9567b8d303eb093c822ce28f1",
"sha256:6bc78fb9c42a716309b4ace56f51965d8b5662c3ba19d4591749f31773db1125",
"sha256:6ebfefebb5c6494a3af41ad8c60248a95da267a24b79ed143723d4502b1fe4d7",
"sha256:720dbcdd3d91c6dfead79c80bf8b00a1d8aa4e5d551dc528c6d5151e4efc3403",
"sha256:732bab78435c48be5d6bc75486ef629d7c8f112e07b313bf1f1a2220ab437277",
"sha256:7947e51ca05489b85928af52a34fe67022ab5b81d4ae32a4109a99e883a0635e",
"sha256:79f5b54f9dc353e5ee47f0c3f02bebd2c899d49780633aa771fed43fa20b3149",
"sha256:80b924edbc012ded8aa8b91cb2fd6207fb1a9a3a377beb4049b8a07445cec6f0",
"sha256:83c5e3eb78ce111c2f0b45f46106cc697c3cb6c4e5f51308e1f81b512c70c8fb",
"sha256:889d4c5c5205a9c90118c1980df526857929841df33e4cd1ff1eff77c6817a65",
"sha256:935ff247b8b78bdf77647fee962b1cc208c51a7b229db30b9ba5f6da3e675178",
"sha256:98b2669c5af842a70cfab33a7043fcb5e7535a690a00cd251b44c9be0be418e5",
"sha256:9e2528823ccf5a0a1d23262dfefe5034dce89cd84e4e124dc553dfcdf63ebb92",
"sha256:bc5e87b72371da87c9bebc68e54882aada9c3114e640de180f62d5da95749cd3",
"sha256:be105382961745f88d8196bba5a69ee2c4455d87ad2a2e5d1eed6bd7fda4d3fd",
"sha256:c06d2e340c7bf8b9ec71da2284beab8519a3908eab031f4ea126e8ccfc3fd567",
"sha256:c2a799e79cee81a862216c47e5623c97b95f1abee8dd1f9eed736df23fb653fb",
"sha256:cfe79844526dd92e3ecc9490b5031fca5f8ab607e1e858feba232b1b788ff0ea",
"sha256:d510aef84d9852653d079c84f2f81a82d5d09815e625f35c95714e7364570ad4",
"sha256:e02a0558e0c2ac8b8bbe6a6ac18c136767ec56b96a321e0dfde2173adfa5a504"
],
"markers": "python_version >= '3.5'",
"version": "==1.1.1"
},
"regex": {
"hashes": [
"sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538",
......@@ -781,28 +738,6 @@
"markers": "python_version >= '3.6'",
"version": "==4.7"
},
"scikit-image": {
"hashes": [
"sha256:1256017c513e8e1b8b9da73e5fd1e605d0077bbbc8e5c8d6c2cab36400131c6c",
"sha256:1cd05c882ffb2a271a1f20b4afe937d63d55b8753c3d652f11495883a7800ebe",
"sha256:23f9178b21c752bfb4e4ea3a3fa0ff79bc5a401bc75ddb4661f2cebd1c2b0e24",
"sha256:2c058770c6ad6e0fe6c30f59970c9c65fa740ff014d121d8c341664cd792cf49",
"sha256:2eea42706a25ae6e0cebaf1914e2ab1c04061b1f3c9966d76025d58a2e9188fc",
"sha256:30447af3f5b7c9491f2d3db5bc275493d1b91bf1dd16b67e2fd79a6bb95d8ee9",
"sha256:3515b890e771f99bbe1051a0dcfe0fc477da961da933c34f89808a0f1eeb7dc2",
"sha256:5f602779258807d03e72c0a439cfb221f647e628be166fb3594397435f13c76b",
"sha256:76446e2402e64d7dba78eeae8aa86e92a0cafe5b1c9e6235bd8d067471ed2788",
"sha256:ae6659b3a8bd4bba7e9dcbfd0064e443b32c7054bf09174749db896730fcf42e",
"sha256:c700336a7f96109c74154090c5e693693a8e3fa09ed6156a5996cdc9a3bb1534",
"sha256:d5ad4a9b4c9797d4c4c48f45fa224c5ebff22b9b0af636c3ecb8addbb66c21e6",
"sha256:d746540cafe7776c6d05a0b40ec744bb8d33d1ddc51faba601d26c02593d8bcc",
"sha256:e972c628ad9ba52c298b032368e29af9bd5eeb81ce33bc2d9b039a81661c99c5",
"sha256:ec25e4110951d3a280421bb10dd510a082ba83d86e20d706294faf7899cdb3d5",
"sha256:fbb618ca911867bce45574c1639618cdfb5d94e207432b19bc19563d80d2f171"
],
"index": "pypi",
"version": "==0.18.1"
},
"scikit-learn": {
"hashes": [
"sha256:0567a2d29ad08af98653300c623bd8477b448fe66ced7198bef4ed195925f082",
......@@ -971,14 +906,6 @@
"markers": "python_version >= '3.5'",
"version": "==2.1.0"
},
"tifffile": {
"hashes": [
"sha256:a5f8caaf672dab0dc8b2609b61d4fd23c6b6fe7e9df38750d4c872962a080ba9",
"sha256:b4ab56ea97755b86cdd5fbd8e49875c949dedfdfa091370366874a0f98beee8c"
],
"markers": "python_version >= '3.7'",
"version": "==2021.1.14"
},
"tqdm": {
"hashes": [
"sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a",
......@@ -1359,11 +1286,11 @@
},
"hypothesis": {
"hashes": [
"sha256:c606c5141ea167a6893ffaea2647a263b93f8db6af28f58b526ce2edce08685a",
"sha256:de3c21d89e2d9e36463a57706b411fad37158f2dc704e357b7f3b1ddd2669aa4"
"sha256:0fee2282d5a586933e448da7b4598260e17ae2bbc9e29017ebfaf11940b9291e",
"sha256:8a9824d862495bbc49274604e2ba13287578159e87a3fd97f8a4249d723f4428"
],
"index": "pypi",
"version": "==6.0.3"
"version": "==6.1.0"
},
"idna": {
"hashes": [
......
......@@ -149,16 +149,16 @@ class project:
scales = []
for vid_index, _ in enumerate(self.videos):
ellipse = deepof.utils.recognize_arena(
self.videos,
vid_index,
path=self.video_path,
arena_type=self.arena,
)[0]
scales.append(
list(
deepof.utils.recognize_arena(
self.videos,
vid_index,
path=self.video_path,
arena_type=self.arena,
)[0]
* 2
)
list(np.array([ellipse[0][0], ellipse[0][1], ellipse[1][1]]) * 2)
+ list(self.arena_dims)
)
......@@ -686,7 +686,7 @@ class coordinates:
# noinspection PyDefaultArgument
def rule_based_annotation(
self, hparams: Dict = {}, video_output: bool = False, frame_limit: int = np.inf
self, hparams: Dict = {}, video_output: bool = False, frame_limit: int = np.inf, debug: bool = False,
) -> Table_dict:
"""Annotates coordinates using a simple rule-based pipeline"""
......@@ -724,6 +724,7 @@ class coordinates:
self._videos,
list(self._tables.keys()).index(idx),
tag_dict[idx],
debug=debug,
frame_limit=frame_limit,
recog_limit=1,
path=os.path.join(self._path, "Videos"),
......
......@@ -133,10 +133,34 @@ def climb_wall(
nose = pos_dict[nose]
def rotate(origin, point, angle):
ox, oy = origin
px, py = point
qx = ox + np.cos(angle) * (px - ox) - np.sin(angle) * (py - oy)
qy = oy + np.sin(angle) * (px - ox) + np.cos(angle) * (py - oy)
return qx, qy
def outside_ellipse(x, y, e_center, e_axes, e_angle, threshold=0.):
x, y = rotate(e_center, (x, y), np.radians(e_angle))
term_x = (x - e_center[0]) ** 2 / (e_axes[0] + threshold) ** 2
term_y = (y - e_center[1]) ** 2 / (e_axes[1] + threshold) ** 2
return term_x + term_y > 1
if arena_type == "circular":
center = np.zeros(2) if centered_data else np.array(arena[:2])
radius = arena[2]
climbing = np.linalg.norm(nose - center, axis=1) > (radius + tol)
center = np.zeros(2) if centered_data else np.array(arena[0])
axes = arena[1]
angle = arena[2]
climbing = outside_ellipse(
x=nose["x"],
y=nose["y"],
e_center=center,
e_axes=axes,
e_angle=-angle,
threshold=tol,
)
else:
raise NotImplementedError("Supported values for arena_type are ['circular']")
......@@ -381,6 +405,7 @@ def get_hparameters(hparams: dict = {}) -> dict:
defaults = {
"speed_pause": 3,
"climb_tol": 10,
"close_contact_tol": 35,
"side_contact_tol": 80,
"follow_frames": 10,
......@@ -548,7 +573,7 @@ def rule_based_tagging(
for _id in animal_ids:
tag_dict[_id + undercond + "climbing"] = deepof.utils.smooth_boolean_array(
climb_wall(arena_type, arena, coords, 1, _id + undercond + "Nose")
climb_wall(arena_type, arena, coords, hparams["climb_tol"], _id + undercond + "Nose")
)
tag_dict[_id + undercond + "speed"] = speeds[_id + undercond + "Center"]
tag_dict[_id + undercond + "huddle"] = deepof.utils.smooth_boolean_array(
......@@ -579,6 +604,7 @@ def tag_rulebased_frames(
undercond,
hparams,
arena,
debug,
):
"""Helper function for rule_based_video. Annotates a given frame with on-screen information
about the recognised patterns"""
......@@ -616,7 +642,8 @@ def tag_rulebased_frames(
if len(animal_ids) > 1:
cv2.circle(frame, (arena[0], arena[1]), arena[2], thickness=2, color=(0, 0, 255))
if debug:
cv2.ellipse(frame, arena[0], arena[1], arena[2], 0, 360, (0, 255, 0), 3)
if tag_dict["nose2nose"][fnum] and not tag_dict["sidebyside"][fnum]:
write_on_frame("Nose-Nose", conditional_pos())
......@@ -690,6 +717,7 @@ def rule_based_video(
recog_limit: int = 1,
path: str = os.path.join("."),
hparams: dict = {},
debug: bool = False,
) -> True:
"""Renders a version of the input video with all rule-based taggings in place.
......@@ -697,6 +725,8 @@ def rule_based_video(
- tracks (list): list containing experiment IDs as strings
- videos (list): list of videos to load, in the same order as tracks
- coordinates (deepof.preprocessing.coordinates): coordinates object containing the project information
- debug (bool): if True, several debugging attributes (such as used body parts and arena) are plotted in
the output video
- vid_index (int): index in videos of the experiment to annotate
- fps (float): frames per second of the analysed video. Same as input by default
- path (str): directory in which the experimental data is stored
......@@ -777,7 +807,8 @@ def rule_based_video(
(w, h),
undercond,
hparams,
(arena, h, w)
(arena, h, w),
debug
)
if writer is None:
......@@ -800,7 +831,5 @@ def rule_based_video(
return True
# TODO:
# - Relativise default contact parameters
# (right now they only work if 'arena_dims' is correctly set in the project object
# - Is border sniffing anything you might consider interesting?
......@@ -19,7 +19,8 @@ import regex as re
from copy import deepcopy
from itertools import combinations, product
from joblib import Parallel, delayed
#from skimage.transform import hough_ellipse
# from skimage.transform import hough_ellipse
from sklearn import mixture
from tqdm import tqdm
from typing import Tuple, Any, List, Union, NewType
......@@ -409,10 +410,14 @@ def circular_arena_recognition(frame: np.array) -> np.array:
# Convert image to greyscale, threshold it, blur it and apply open-close operations
gray_image = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray_image, 100, 255, 0)
ret, thresh = cv2.threshold(gray_image, 80, 255, 0)
frame = cv2.medianBlur(thresh, 9)
frame = cv2.morphologyEx(frame, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (80, 80)))
frame = cv2.morphologyEx(frame, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (80, 80)))
frame = cv2.morphologyEx(
frame, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (80, 80))
)
frame = cv2.morphologyEx(
frame, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (80, 80))
)
# Find contours in the processed image
cnts, _ = cv2.findContours(frame, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
......@@ -423,31 +428,9 @@ def circular_arena_recognition(frame: np.array) -> np.array:
axes_length = tuple([int(i) // 2 for i in ellipse_params[1]])
angle = ellipse_params[2]
# circle = cv2.HoughCircles(
# frame,
# # accuracy=20,
# # threshold=250,
# # min_size=frame.shape[0] // 6,
# # max_size=frame.shape[0] // 2,
# cv2.HOUGH_GRADIENT,
# 1,
# 300,
# param1=50,
# param2=10,
# minRadius=0,
# maxRadius=frame.shape[0] // 2,
# )
# #result.sort(order='accumulator')
#
# circles = []
#
# if circle is not None:
# circle = np.uint16(np.around(circle[0]))
# circles.append(circle)
# return circles[0]
return center_coordinates, axes_length, angle
def rolling_speed(
dframe: pd.DatetimeIndex,
window: int = 5,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment