Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

some fixes #317

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
163b6e1
checkpoint for computer switching
tzukpolinsky Nov 1, 2023
914aef2
1. added the directionality by body part to the config creation
tzukpolinsky Nov 8, 2023
9559069
Merge remote-tracking branch 'origin/master'
tzukpolinsky Nov 8, 2023
4b98366
preventing error in config creation
tzukpolinsky Nov 12, 2023
1094983
preventing error in seaborn when creating plots, because in seabrom 0…
tzukpolinsky Nov 12, 2023
c4ffdac
I create my body part directionality in a sub folder inside the body_…
tzukpolinsky Nov 13, 2023
219f7cf
I create my body part directionality in a sub folder inside the body_…
tzukpolinsky Nov 13, 2023
ec25329
I create my body part directionality in a sub folder inside the body_…
tzukpolinsky Nov 13, 2023
b374433
changes:
tzukpolinsky Dec 3, 2023
29c3f10
changes:
tzukpolinsky Dec 7, 2023
8d4888c
added max depth and cleaned duplicated values in enums
tzukpolinsky Dec 21, 2023
b965472
Merge pull request #1
tzukpolinsky Dec 21, 2023
6c6bc80
Merge remote-tracking branch 'upstream/master'
tzukpolinsky Dec 21, 2023
d7c31ff
trying to merge
tzukpolinsky Dec 21, 2023
0163e02
Merge branch 'master' of https://github.com/tzukpolinsky/simba
tzukpolinsky Dec 21, 2023
a43553b
trying to merge
tzukpolinsky Dec 23, 2023
0a54a82
Merge branch 'master' of https://github.com/sgoldenlab/simba
tzukpolinsky Dec 23, 2023
e826fda
Merge branch 'master' of https://github.com/sgoldenlab/simba
tzukpolinsky Jan 1, 2024
3899965
fixing missing files paths for app visibility
tzukpolinsky Jan 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions simba/SimBA.py
Original file line number Diff line number Diff line change
Expand Up @@ -1147,8 +1147,8 @@ def activate(box, *args):
button_skipOC.grid(row=2, sticky=W, pady=5)

label_extractfeatures.grid(row=0, column=0, sticky=NW)
button_extractfeatures.grid(row=0, column=0, sticky=NW)
labelframe_usrdef.grid(row=1, column=0, sticky=NW, pady=5)
button_extractfeatures.grid(row=1, column=0, sticky=NW)
labelframe_usrdef.grid(row=0, column=0, sticky=NW, pady=15)
userscript.grid(row=1, column=0, sticky=NW)
self.scriptfile.grid(row=2, column=0, sticky=NW)

Expand Down
145 changes: 145 additions & 0 deletions simba/feature_extractors/feature_extractor_freezing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
__author__ = "Tzuk Polinsky"

import os
from itertools import product

import numpy as np
import pandas as pd

from simba.mixins.config_reader import ConfigReader
from simba.mixins.feature_extraction_mixin import FeatureExtractionMixin
from simba.utils.checks import check_str
from simba.utils.enums import Paths
from simba.utils.printing import SimbaTimer, stdout_success
from simba.utils.read_write import get_fn_ext, read_df, write_df


class MiceFreezingFeatureExtractor(ConfigReader, FeatureExtractionMixin):
"""
Generic featurizer of data within SimBA project using user-defined body-parts in the pose-estimation data.
Results are stored in the `project_folder/csv/features_extracted` directory of the SimBA project.

:parameter str config_path: path to SimBA project config file in Configparser format

.. note::
`Feature extraction tutorial <https://github.com/sgoldenlab/simba/blob/master/docs/tutorial.md#step-5-extract-features>`__.

Examples
----------
>>> feature_extractor = MiceFreezingFeatureExtractor(config_path='MyProjectConfig')
>>> feature_extractor.run()
"""

def __init__(self, config_path: str):
FeatureExtractionMixin.__init__(self, config_path=config_path)
ConfigReader.__init__(self, config_path=config_path)
print(
"Extracting features from {} file(s)...".format(str(len(self.files_found)))
)

# Function to calculate the direction vector
def angle_between_vectors(self, v1, v2):
unit_vector_1 = v1 / np.linalg.norm(v1)
unit_vector_2 = v2 / np.linalg.norm(v2)
dot_product = unit_vector_2.dot(unit_vector_1.T)
angle = np.arccos(dot_product)
return np.degrees(angle)

def calculate_direction_vector(self, from_point, to_point):
return np.array(to_point) - np.array(from_point)

def extract_features(self, input_file_path: str, window_size: int, video_center: [int, int], pixel_mm: float,directionality_data:pd.DataFrame):
print("Calculating freezing features ...")

input_data = pd.read_csv(input_file_path)
output_data = pd.DataFrame(
columns=["activity"])
columns_to_drop = [col for col in input_data.columns if ('bug' in col) or ("_p" in col)]
columns_to_drop.append("Unnamed: 0")
without_bug = input_data.drop(columns_to_drop, axis=1)

body_parts_diffs = without_bug.diff(axis=0)
body_parts_diffs["nose_x"]*=5
body_parts_diffs["nose_y"]*=5

time_point_diff = body_parts_diffs.sum(axis=1)
rolling_windows = time_point_diff.rolling(window=window_size, min_periods=1).sum()
output_data["activity"] = rolling_windows.abs().fillna(0)
center_cols = [colName for colName in without_bug.columns if ("center" in colName) and ("_p") not in colName]
nose_cols = [colName for colName in without_bug.columns if ("nose" in colName) and ("_p") not in colName]
centers = without_bug[center_cols].to_numpy()
noses = without_bug[nose_cols].to_numpy()
angles = []
for i, center in enumerate(centers):
nose = noses[i]
vector_fixed_to_center = self.calculate_direction_vector(video_center, center)
vector_center_to_nose = self.calculate_direction_vector(center, nose)
angles.append(self.angle_between_vectors(vector_center_to_nose, vector_fixed_to_center))
# output_data["nose_direction"] = angles
angles_df = pd.DataFrame(angles)
# angles_diff = angles_df.diff()
# angles_diff_sum = angles_diff.rolling(window=window_size, min_periods=1).sum()
# output_data["nose_direction_sum_of_diffs"] = angles_diff_sum.abs().fillna(0)
output_data["nose_direction_avg"] = angles_df.rolling(window=window_size, min_periods=1).mean().fillna(0)
# directionality_rolling = directionality_data.rolling(window=window_size, min_periods=1)
# output_data["amount_of_looking_at_bug"] = directionality_rolling.sum()
# onsets = [-1] * len(output_data["amount_of_looking_at_bug"])
# for j, rol in enumerate(directionality_rolling):
# for i, r in enumerate(rol):
# if r:
# onsets[j] = i
# break
# output_data["looking_at_bug_onset"] = onsets
return output_data

def run(self):
"""
Method to compute and save features to disk. Results are saved in the `project_folder/csv/features_extracted`
directory of the SimBA project.

Returns
-------
None
"""
self.roi_coordinates_path = os.path.join(
self.logs_path, Paths.ROI_DEFINITIONS.value
)
polygons = pd.read_hdf(self.roi_coordinates_path, key="polygons")
directionality_dir_path = os.path.join(self.body_part_directionality_df_dir, "bug")
for file_cnt, file_path in enumerate(self.files_found):
video_timer = SimbaTimer(start=True)
print(
"Extracting features for video {}/{}...".format(
str(file_cnt + 1), str(len(self.files_found))
)
)
_, file_name, _ = get_fn_ext(file_path)
current_polygon = polygons[polygons["Video"] == file_name]
directionality_data_path = os.path.join(directionality_dir_path, file_name + ".csv")
directionality_data = pd.read_csv(directionality_data_path)["Directing_BOOL"]
check_str("file name", file_name)
video_settings, self.px_per_mm, fps = self.read_video_info(
video_name=file_name
)
self.data_df = self.extract_features(file_path, 50, (
current_polygon["Center_X"].values[0], current_polygon["Center_Y"].values[0]),
video_settings["pixels/mm"].values[0],directionality_data)
save_path = os.path.join(self.save_dir, file_name + "." + self.file_type)
self.data_df = self.data_df.reset_index(drop=True).fillna(0)
write_df(df=self.data_df, file_type=self.file_type, save_path=save_path)
video_timer.stop_timer()
print(
f"Feature extraction complete for video {file_name} (elapsed time: {video_timer.elapsed_time_str}s)"
)
print(
f"Feature extraction file for video {file_name} saved to {save_path})"
)

self.timer.stop_timer()
stdout_success(
f"Feature extraction complete for {str(len(self.files_found))} video(s). Results are saved inside the project_folder/csv/features_extracted directory",
elapsed_time=self.timer.elapsed_time_str,
)

# test = UserDefinedFeatureExtractor(config_path='/Users/simon/Desktop/envs/troubleshooting/two_black_animals_14bp/project_folder/project_config.ini')
# test.run()
4 changes: 2 additions & 2 deletions simba/feature_extractors/feature_subsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ def append_to_data(self):
self.features_extracted_temp_path + f"/*.{self.file_type}"
)
self.data_df = self.read_all_files_in_folder_mp_futures(
file_paths=file_paths, file_type=self.file_type
annotations_file_paths=file_paths, file_type=self.file_type
)
self.check_raw_dataset_integrity(
df=self.data_df, logs_path=self.logs_path
Expand All @@ -469,7 +469,7 @@ def append_to_data(self):
self.targets_inserted_temp_path + f"/*.{self.file_type}"
)
self.data_df = self.read_all_files_in_folder_mp_futures(
file_paths=file_paths, file_type=self.file_type
annotations_file_paths=file_paths, file_type=self.file_type
)
self.check_raw_dataset_integrity(
df=self.data_df, logs_path=self.logs_path
Expand Down
1 change: 1 addition & 0 deletions simba/mixins/config_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,7 @@ def drop_bp_cords(self, df: pd.DataFrame) -> pd.DataFrame:
BodypartColumnNotFoundWarning(
msg=f"SimBA could not drop body-part coordinates, some body-part names are missing in dataframe. SimBA expected the following body-parts, that could not be found inside the file: {missing_body_part_fields}"
)
return df
else:
return df.drop(self.bp_col_names, axis=1)

Expand Down
Loading