diff --git a/deepof/utils.py b/deepof/utils.py index 32c8ff14866de03c813253f8d957ad7b09f3df95..00a6544aa002273e769efa573e5f697ada982537 100644 --- a/deepof/utils.py +++ b/deepof/utils.py @@ -8,6 +8,7 @@ Functions and general utilities for the deepof package. See documentation for de """ +import argparse import cv2 import matplotlib.pyplot as plt import multiprocessing @@ -72,6 +73,26 @@ def connect_mouse_topview(animal_id=None) -> nx.Graph: # QUALITY CONTROL AND PREPROCESSING # +def str2bool(v: str) -> bool: + """Returns the passed string as a boolean + + Parameters: + v (str): string to transform to boolean value + + Returns: + boolean value. If conversion is not possible, it raises an error + """ + + if isinstance(v, bool): + return v + if v.lower() in ("yes", "true", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "f", "n", "0"): + return False + else: + raise argparse.ArgumentTypeError("Boolean compatible value expected.") + + def likelihood_qc(dframe: pd.DataFrame, threshold: float = 0.9) -> np.array: """Returns a DataFrame filtered dataframe, keeping only the rows entirely above the threshold. diff --git a/examples/model_training.py b/examples/model_training.py index 9947d32f66069969a6a8e95e7346409d16575b6f..22759e008c8ec41fa7d176d7cec48ff753d1b074 100644 --- a/examples/model_training.py +++ b/examples/model_training.py @@ -1,24 +1,13 @@ # @author lucasmiranda42 from datetime import datetime -from deepof.preprocess import * +import deepof.preprocess from deepof.models import * from tensorflow import keras import argparse import os, pickle -def str2bool(v): - if isinstance(v, bool): - return v - if v.lower() in ("yes", "true", "t", "y", "1"): - return True - elif v.lower() in ("no", "false", "f", "n", "0"): - return False - else: - raise argparse.ArgumentTypeError("Boolean value expected.") - - parser = argparse.ArgumentParser( description="Autoencoder training for DeepOF animal pose recognition" ) @@ -60,7 +49,7 @@ parser.add_argument( "-v", help="Sets the model to train to a variational Bayesian autoencoder. Defaults to True", default=True, - type=str2bool, + type=deepof.preprocess.str2bool, ) parser.add_argument( "--loss", @@ -102,7 +91,7 @@ parser.add_argument( "-ol", help="If True, adds the negative MMD between all components of the latent Gaussian mixture to the loss function", default=False, - type=str2bool, + type=deepof.preprocess.str2bool, ) parser.add_argument( "--batch-size", @@ -201,7 +190,7 @@ bp_dict = { "B_Tail_base": ["B_Center", "B_Left_flank", "B_Right_flank"], } -DLC_social_1_coords = project( +DLC_social_1_coords = deepof.preprocess.project( path=train_path, # Path where to find the required files smooth_alpha=0.50, # Alpha value for exponentially weighted smoothing distances=[ @@ -228,10 +217,10 @@ DLC_social_1_coords = project( coords1 = DLC_social_1_coords.get_coords(center="B_Center", align="B_Nose") distances1 = DLC_social_1_coords.get_distances() angles1 = DLC_social_1_coords.get_angles() -coords_distances1 = merge_tables(coords1, distances1) -coords_angles1 = merge_tables(coords1, angles1) -dists_angles1 = merge_tables(distances1, angles1) -coords_dist_angles1 = merge_tables(coords1, distances1, angles1) +coords_distances1 = deepof.preprocess.merge_tables(coords1, distances1) +coords_angles1 = deepof.preprocess.merge_tables(coords1, angles1) +dists_angles1 = deepof.preprocess.merge_tables(distances1, angles1) +coords_dist_angles1 = deepof.preprocess.merge_tables(coords1, distances1, angles1) input_dict_train = { @@ -302,7 +291,7 @@ input_dict_train = { # If a validation path is specified, use it to build a validation set if val_path: - DLC_social_2_coords = project( + DLC_social_2_coords = deepof.preprocess.project( path=val_path, # Path where to find the required files smooth_alpha=0.50, # Alpha value for exponentially weighted smoothing distances=[ @@ -328,10 +317,10 @@ if val_path: coords2 = DLC_social_2_coords.get_coords(center="B_Center", align="B_Nose") distances2 = DLC_social_2_coords.get_distances() angles2 = DLC_social_2_coords.get_angles() - coords_distances2 = merge_tables(coords2, distances2) - coords_angles2 = merge_tables(coords2, angles2) - dists_angles2 = merge_tables(distances2, angles2) - coords_dist_angles2 = merge_tables(coords2, distances2, angles2) + coords_distances2 = deepof.preprocess.merge_tables(coords2, distances2) + coords_angles2 = deepof.preprocess.merge_tables(coords2, angles2) + dists_angles2 = deepof.preprocess.merge_tables(distances2, angles2) + coords_dist_angles2 = deepof.preprocess.merge_tables(coords2, distances2, angles2) input_dict_val = { "coords": coords2.preprocess( diff --git a/examples/predict_video.py b/examples/predict_video.py index ecbc5e4b3d1dc6398bc4402f61986e08861959ec..1ce1eea7dd1d9d6cefcc5e210b7d29935161c942 100644 --- a/examples/predict_video.py +++ b/examples/predict_video.py @@ -5,22 +5,12 @@ import sys sys.path.insert(1, "../") -from deepof.preprocess import * -from deepof.models import * +import deepof.preprocess import argparse import cv2 -import os, pickle - - -def str2bool(v): - if isinstance(v, bool): - return v - if v.lower() in ("yes", "true", "t", "y", "1"): - return True - elif v.lower() in ("no", "false", "f", "n", "0"): - return False - else: - raise argparse.ArgumentTypeError("Boolean value expected.") +import os +import pickle +from deepof.models import * parser = argparse.ArgumentParser( @@ -49,14 +39,14 @@ parser.add_argument( "-pred", help="Activates the prediction branch of the variational Seq 2 Seq model. Defaults to True", default=True, - type=str2bool, + type=deepof.preprocess.str2bool, ) parser.add_argument( "--variational", "-v", help="Sets the model to train to a variational Bayesian autoencoder. Defaults to True", default=True, - type=str2bool, + type=deepof.preprocess.str2bool, ) parser.add_argument( "--loss", @@ -161,7 +151,7 @@ bp_dict = { "B_Tail_base": ["B_Center", "B_Left_flank", "B_Right_flank"], } -DLC_social = project( +DLC_social = deepof.preprocess.project( path=os.path.join(data_path), # Path where to find the required files smooth_alpha=0.50, # Alpha value for exponentially weighted smoothing distances=[ @@ -183,20 +173,19 @@ DLC_social = project( table_format=".h5", ) - DLC_social_coords = DLC_social.run(verbose=True) # Coordinates for training data coords1 = DLC_social_coords.get_coords(center="B_Center", align="B_Nose") distances1 = DLC_social_coords.get_distances() angles1 = DLC_social_coords.get_angles() -coords_distances1 = merge_tables(coords1, distances1) -coords_angles1 = merge_tables(coords1, angles1) -dists_angles1 = merge_tables(distances1, angles1) -coords_dist_angles1 = merge_tables(coords1, distances1, angles1) +coords_distances1 = deepof.preprocess.merge_tables(coords1, distances1) +coords_angles1 = deepof.preprocess.merge_tables(coords1, angles1) +dists_angles1 = deepof.preprocess.merge_tables(distances1, angles1) +coords_dist_angles1 = deepof.preprocess.merge_tables(coords1, distances1, angles1) input_dict = { - "coords": table_dict( + "coords": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -207,7 +196,7 @@ input_dict = { shuffle=False, align="center", ), - "dists": table_dict( + "dists": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -218,7 +207,7 @@ input_dict = { shuffle=False, align="center", ), - "angles": table_dict( + "angles": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -229,7 +218,7 @@ input_dict = { shuffle=False, align="center", ), - "coords+dist": table_dict( + "coords+dist": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -240,7 +229,7 @@ input_dict = { shuffle=False, align="center", ), - "coords+angle": table_dict( + "coords+angle": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -251,7 +240,7 @@ input_dict = { shuffle=False, align="center", ), - "dists+angle": table_dict( + "dists+angle": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -262,7 +251,7 @@ input_dict = { shuffle=False, align="center", ), - "coords+dist+angle": table_dict( + "coords+dist+angle": deepof.preprocess.table_dict( ((k, coords1[k]) for k in [video_name]), typ="coords" ).preprocess( window_size=13, @@ -290,7 +279,9 @@ ae.build(input_dict[input_type].shape) ae.load_weights(model_path) # Predict cluster labels per frame -frame_labels = np.argmax(grouper.predict(input_dict[input_type]), axis=1) +frame_labels = deepof.preprocess.np.argmax( + grouper.predict(input_dict[input_type]), axis=1 +) print(frame_labels) print(frame_labels.shape) @@ -308,7 +299,7 @@ cap = cv2.VideoCapture( # Loop over the first frames in the video to get resolution and center of the arena if frame_limit == -1: - frame_limit = np.inf + frame_limit = deepof.preprocess.np.inf fnum, h, w = 0, 0, 0 if action == "save": diff --git a/tests/test_utils.py b/tests/test_utils.py index fd9418ec2ed3ada5ca45cf7c47116230a88154fa..a90d878691026b80046d269d885618ccfda31794 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -33,6 +33,25 @@ def autocorr(x, t=1): # QUALITY CONTROL AND PREPROCESSING # +@settings(deadline=None) +@given( + v=st.one_of( + st.just("yes"), + st.just("true"), + st.just("t"), + st.just("y"), + st.just("1"), + st.just("no"), + st.just("false"), + st.just("f"), + st.just("n"), + st.just("0"), + ) +) +def test_str2bool(v): + assert type(str2bool(v)) == bool + + @settings(deadline=None) @given( mult=st.integers(min_value=1, max_value=10),