diff --git a/setup.py b/setup.py index 2ee9c479d..fcc7c8424 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ # Setup configuration setuptools.setup( name="Simba-UW-tf-dev", - version="2.1.6", + version="2.1.7", author="Simon Nilsson, Jia Jie Choong, Sophia Hwang", author_email="sronilsson@gmail.com", description="Toolkit for computer classification and analysis of behaviors in experimental animals", diff --git a/simba/data_processors/cuda/image.py b/simba/data_processors/cuda/image.py index c63ba8d89..deed866cf 100644 --- a/simba/data_processors/cuda/image.py +++ b/simba/data_processors/cuda/image.py @@ -395,6 +395,10 @@ def stack_sliding_mse(x: np.ndarray, For CPU function see :func:`~simba.mixins.image_mixin.ImageMixin.img_stack_mse` and :func:`~simba.mixins.image_mixin.ImageMixin.img_sliding_mse`. + .. math:: + + \text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + :param np.ndarray x: Input array of images, where the first dimension corresponds to the stack of images. The array should be either 3D (height, width, channels) or 4D (batch, height, width, channels). :param Optional[int] stride: The stride or step size for the sliding window that determines the reference image. Defaults to 1, meaning the previous image in the stack is used as the reference. :param Optional[int] batch_size: The number of images to process in a single batch. Larger batch sizes may improve performance but require more GPU memory. Defaults to 1000. diff --git a/simba/third_party_label_appenders/BORIS_appender.py b/simba/third_party_label_appenders/BORIS_appender.py index 686e54a3a..730fd3e09 100644 --- a/simba/third_party_label_appenders/BORIS_appender.py +++ b/simba/third_party_label_appenders/BORIS_appender.py @@ -71,6 +71,8 @@ def run(self): else: video_annot = boris_annotation_dict[self.file_name] data_df = read_df(file_path, self.file_type) + video_annot = video_annot.fillna(len(data_df)) + print(video_annot) for clf_name in self.clf_names: data_df[clf_name] = 0 if clf_name not in video_annot[BEHAVIOR].unique(): @@ -101,23 +103,7 @@ def __save_boris_annotations(self, df): # #test.create_boris_master_file() # test.run() - - - -# test = BorisAppender(config_path='/Users/simon/Desktop/envs/troubleshooting/two_black_animals_14bp/project_folder/project_config.ini', -# data_dir='/Users/simon/Downloads/FIXED') -# test.create_boris_master_file() -# test.run() - -# test = BorisAppender(config_path='/Users/simon/Desktop/troubleshooting/train_model_project/project_folder/project_config.ini', boris_folder=r'/Users/simon/Desktop/troubleshooting/train_model_project/boris_import') -# test.create_boris_master_file() -# test.append_boris() - -# test = BorisAppender(config_path='/Users/simon/Desktop/envs/marcel_boris/project_folder/project_config.ini', boris_folder=r'/Users/simon/Desktop/envs/marcel_boris/BORIS_data') -# test.create_boris_master_file() -# test.append_boris() - -# test = BorisAppender(config_path='/Users/simon/Desktop/envs/troubleshooting/two_black_animals_14bp/project_folder/project_config.ini', -# boris_folder='/Users/simon/Downloads/FIXED') -# test.create_boris_master_file() -# test.append_boris() +# +# test = BorisAppender(config_path=r"C:\troubleshooting\two_black_animals_14bp\project_folder\project_config.ini", +# data_dir=r"C:\troubleshooting\two_black_animals_14bp\BORIS") +# test.run() \ No newline at end of file diff --git a/simba/third_party_label_appenders/third_party_appender.py b/simba/third_party_label_appenders/third_party_appender.py index 964bc8e9b..5ab2897a1 100644 --- a/simba/third_party_label_appenders/third_party_appender.py +++ b/simba/third_party_label_appenders/third_party_appender.py @@ -212,7 +212,7 @@ def run(self): # test.run() # -# + # log = True # file_format = 'xlsx' # error_settings = {'INVALID annotations file data format': 'WARNING', diff --git a/simba/utils/read_write.py b/simba/utils/read_write.py index 56375981e..f10004e2a 100644 --- a/simba/utils/read_write.py +++ b/simba/utils/read_write.py @@ -2239,14 +2239,12 @@ def _find_cap_insensitive_name(target: str, values: List[str]) -> Union[None, st return None else: return values[values_lower.index(target_lower)] - def read_boris_file(file_path: Union[str, os.PathLike], fps: Optional[Union[int, float]] = None, orient: Optional[Literal['index', 'columns']] = 'index', save_path: Optional[Union[str, os.PathLike]] = None, raise_error: Optional[bool] = False, log_setting: Optional[bool] = False) -> Union[None, Dict[str, Dict[str, pd.DataFrame]]]: - """ Reads a BORIS behavioral annotation file, processes the data, and optionally saves the results to a file. @@ -2270,6 +2268,7 @@ def read_boris_file(file_path: Union[str, os.PathLike], FRAME = 'FRAME' STOP = 'STOP' STATUS = "Status" + FRAME_INDEX = 'Image index' MEDIA_FILE_PATH = "Media file path" check_file_exist_and_readable(file_path=file_path) @@ -2283,16 +2282,20 @@ def read_boris_file(file_path: Union[str, os.PathLike], expected_headers = [TIME, MEDIA_FILE_PATH, BEHAVIOR, STATUS] if not OBSERVATION_ID in boris_df.columns: if raise_error: - raise InvalidFileTypeError(msg=f'{file_path} is not a valid BORIS file', source=read_boris_file.__name__) + raise InvalidFileTypeError(msg=f'{file_path} is not a valid BORIS file', + source=read_boris_file.__name__) else: - ThirdPartyAnnotationsInvalidFileFormatWarning(annotation_app="BORIS", file_path=file_path, source=read_boris_file.__name__, log_status=log_setting) + ThirdPartyAnnotationsInvalidFileFormatWarning(annotation_app="BORIS", file_path=file_path, + source=read_boris_file.__name__, log_status=log_setting) return {} start_idx = boris_df[boris_df[OBSERVATION_ID] == TIME].index.values if len(start_idx) != 1: if raise_error: - raise InvalidFileTypeError(msg=f'{file_path} is not a valid BORIS file', source=read_boris_file.__name__) + raise InvalidFileTypeError(msg=f'{file_path} is not a valid BORIS file', + source=read_boris_file.__name__) else: - ThirdPartyAnnotationsInvalidFileFormatWarning(annotation_app="BORIS", file_path=file_path, source=read_boris_file.__name__, log_status=log_setting) + ThirdPartyAnnotationsInvalidFileFormatWarning(annotation_app="BORIS", file_path=file_path, + source=read_boris_file.__name__, log_status=log_setting) return {} df = pd.read_csv(file_path, skiprows=range(0, int(start_idx + 1))) else: @@ -2303,19 +2306,25 @@ def read_boris_file(file_path: Union[str, os.PathLike], numeric_check = pd.to_numeric(df[TIME], errors='coerce').notnull().all() if not numeric_check: if raise_error: - raise InvalidInputError(msg=f'SimBA found TIME DATA annotation in file {file_path} that could not be interpreted as numeric values (seconds or frame numbers)') + raise InvalidInputError( + msg=f'SimBA found TIME DATA annotation in file {file_path} that could not be interpreted as numeric values (seconds or frame numbers)') else: ThirdPartyAnnotationsInvalidFileFormatWarning(annotation_app="BORIS", file_path=file_path, source=read_boris_file.__name__, log_status=log_setting) return {} df[TIME] = df[TIME].astype(np.float32) media_file_names_in_file = df[MEDIA_FILE_PATH].unique() + FRAME_INDEX = _find_cap_insensitive_name(target=FRAME_INDEX, values=list(df.columns)) if fps is None: FPS = _find_cap_insensitive_name(target=FPS, values=list(df.columns)) if not FPS in df.columns: if raise_error: - raise FrameRangeError(f'The annotations are in seconds and FPS was not passed. FPS could also not be read from the BORIS file', source=read_boris_file.__name__) + raise FrameRangeError( + f'The annotations are in seconds and FPS was not passed. FPS could also not be read from the BORIS file', + source=read_boris_file.__name__) else: - FrameRangeWarning(msg=f'The annotations are in seconds and FPS was not passed. FPS could also not be read from the BORIS file', source=read_boris_file.__name__) + FrameRangeWarning( + msg=f'The annotations are in seconds and FPS was not passed. FPS could also not be read from the BORIS file', + source=read_boris_file.__name__) ThirdPartyAnnotationsInvalidFileFormatWarning(annotation_app="BORIS", file_path=file_path, source=read_boris_file.__name__, log_status=log_setting) return {} if len(media_file_names_in_file) == 1: @@ -2328,28 +2337,37 @@ def read_boris_file(file_path: Union[str, os.PathLike], for fps_value in fps_lst: check_float(name='fps', value=fps_value, min_value=10e-6, raise_error=True) fps.append(float(fps_value)) + if FRAME_INDEX is not None: + expected_headers.append(FRAME_INDEX) df = df[expected_headers] - results = {} for video_cnt, video_file_name in enumerate(media_file_names_in_file): video_name = get_fn_ext(filepath=video_file_name)[1] results[video_name] = {} video_fps = fps[video_cnt] video_df = df[df[MEDIA_FILE_PATH] == video_file_name].reset_index(drop=True) - video_df['FRAME'] = (df[TIME] * video_fps).astype(int) + if FRAME_INDEX is None: + video_df['FRAME'] = (video_df[TIME] * video_fps).astype(int) + else: + video_df['FRAME'] = video_df[FRAME_INDEX] video_df = video_df.drop([TIME, MEDIA_FILE_PATH], axis=1) video_df = video_df.rename(columns={BEHAVIOR: 'BEHAVIOR', STATUS: EVENT}) for clf in video_df['BEHAVIOR'].unique(): video_clf_df = video_df[video_df['BEHAVIOR'] == clf].reset_index(drop=True) if orient == 'index': - start_clf, stop_clf = video_clf_df[video_clf_df[EVENT] == START].reset_index(drop=True), video_clf_df[video_clf_df[EVENT] == STOP].reset_index(drop=True) + start_clf, stop_clf = video_clf_df[video_clf_df[EVENT] == START].reset_index(drop=True), video_clf_df[ + video_clf_df[EVENT] == STOP].reset_index(drop=True) start_clf = start_clf.rename(columns={FRAME: START}).drop([EVENT, 'BEHAVIOR'], axis=1) stop_clf = stop_clf.rename(columns={FRAME: STOP}).drop([EVENT], axis=1) if len(start_clf) != len(stop_clf): if raise_error: - raise FrameRangeError(f'In file {file_path}, the number of start events ({len(start_clf)}) and stop events ({len(stop_clf)}) for behavior {clf} and video {video_name} is not equal', source=read_boris_file.__name__) + raise FrameRangeError( + f'In file {file_path}, the number of start events ({len(start_clf)}) and stop events ({len(stop_clf)}) for behavior {clf} and video {video_name} is not equal', + source=read_boris_file.__name__) else: - FrameRangeWarning(msg=f'In file {file_path}, the number of start events ({len(start_clf)}) and stop events ({len(stop_clf)}) for behavior {clf} and video {video_name} is not equal', source=read_boris_file.__name__) + FrameRangeWarning( + msg=f'In file {file_path}, the number of start events ({len(start_clf)}) and stop events ({len(stop_clf)}) for behavior {clf} and video {video_name} is not equal', + source=read_boris_file.__name__) return results video_clf_df = pd.concat([start_clf, stop_clf], axis=1)[['BEHAVIOR', START, STOP]] results[video_name][clf] = video_clf_df