import io
import sys
import warnings
from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Optional, Union
import imageio.v3 as iio
import matplotlib.pyplot as plt
from .utils import (
construct_dlc_output_style_df_from_dictionary,
convert_to_path,
read_config,
)
[docs]class MarkerDetection(ABC):
"""
Class to run marker detection using different marker detection methods.
Parameters
----------
object_to_analyse: Path or str
The path to the video to be analysed.
output_directory: Path or str
The directory, in which the output file will be stored.
marker_detection_directory: Path or str, optional
The filepath to the config file to use for marker detection. E.g., the
DLC project config file. None, for manual Marker Detection.
Attributes
__________
object_to_analyse: Path
The path to the video to be analysed.
output_directory: Path
The directory, in which the output file will be stored.
marker_detection_directory: Path
The filepath to the config file to use for marker detection. E.g., the
DLC project config file. None, for manual Marker Detection.
Methods
_______
analyze_objects(filepath, labels, only_first_frame, filtering, use_gpu):
Abstract method for subclasses to be implemented using the
corresponding marker detection method.
"""
def __init__(
self,
object_to_analyse: Union[Path, str],
output_directory: Union[Path, str],
marker_detection_directory: Optional[Union[Path, str]] = None,
) -> None:
"""
Construct all necessary attributes for class MarkerDetection.
Parameters
----------
object_to_analyse: Path or str
The path to the video to be analysed.
output_directory: Path or str
The directory, in which the output file will be stored.
marker_detection_directory: Path or str, optional
The filepath to the config file to use for marker detection. E.g., the
DLC project config file. None, for manual Marker Detection.
"""
self.object_to_analyse = convert_to_path(object_to_analyse)
self.output_directory = convert_to_path(output_directory)
if type(marker_detection_directory) is not None:
self.marker_detection_directory = convert_to_path(
marker_detection_directory
)
[docs] @abstractmethod
def analyze_objects(
self,
filepath: Path,
labels: Optional[List[str]] = None,
only_first_frame: bool = False,
filtering: bool = False,
use_gpu: str = "") -> Path:
pass
[docs]class DeeplabcutInterface(MarkerDetection):
[docs] def analyze_objects(self, filepath: Path, filtering: bool = False, use_gpu: str = "", **kwargs) -> Path:
filepath = convert_to_path(filepath)
if use_gpu == "prevent": # limit GPU use
import tensorflow.compat.v1 as tf
sess = tf.Session(config=tf.ConfigProto(device_count={"GPU": 0}))
elif use_gpu == "low":
import tensorflow.compat.v1 as tf
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.1)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
elif use_gpu == "full":
import tensorflow.compat.v1 as tf
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=1)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options))
old_stdout = sys.stdout # mute deeplabcut
text_trap = io.StringIO()
sys.stdout = text_trap
with warnings.catch_warnings():
warnings.simplefilter("ignore")
import deeplabcut as dlc
dlc_ending = dlc.analyze_videos(
config=str(self.marker_detection_directory),
videos=[str(self.object_to_analyse)],
destfolder=str(self.output_directory),
)
if filtering:
dlc.filterpredictions(
config=str(self.marker_detection_directory),
video=[str(self.object_to_analyse)],
save_as_csv=False,
)
unfiltered_filepath = self.output_directory.joinpath(
self.object_to_analyse.stem + dlc_ending + ".h5"
)
unfiltered_filepath.rename(filepath.with_suffix(".h5"))
if filtering:
filtered_filepath = self.output_directory.joinpath(
self.object_to_analyse.stem + dlc_ending + "_filtered.h5"
)
if filtered_filepath.exists():
filtered_filepath.rename(
self.output_directory.joinpath(filepath.stem + "_filtered.h5")
)
else:
print(f"{filtered_filepath} not found! Data was analysed but not filtered.")
sys.stdout = old_stdout # unmute DLC
return filepath
[docs]class ManualAnnotation(MarkerDetection):
[docs] def analyze_objects(self, filepath: Path, labels: Optional[List[str]] = None, only_first_frame: bool = False,
**kwargs) -> Path:
if labels is None:
list_of_labels = read_config(self.marker_detection_directory)
else:
list_of_labels = labels
frames_annotated = {}
for label in list_of_labels:
frames_annotated[label] = {"x": [], "y": [], "likelihood": []}
for frame in iio.imiter(self.object_to_analyse):
plt.close()
fig, ax = plt.subplots(figsize=(10, 7))
plt.imshow(frame)
y_lim = frame.shape[0]
x_lim = frame.shape[1]
x_ticks = [i for i in range(x_lim) if i % 10 == 0]
y_ticks = [i for i in range(y_lim) if i % 10 == 0]
x_labels = [i if i % 50 == 0 else " " for i in x_ticks]
y_labels = [i if i % 50 == 0 else " " for i in y_ticks]
ax.set_xticks(x_ticks, labels=x_labels)
ax.set_yticks(y_ticks, labels=y_labels)
plt.grid(visible=True, c="black", alpha=0.25)
plt.show()
for label in list_of_labels:
likelihood = 1
y = input(
f"{label}: y_or_row_index\nIf you want to skip this marker, enter x!"
)
if y == "x":
likelihood, x, y = 0, 0, 0
else:
x = input(f"{label}: x_or_column_index")
try:
int(x)
int(y)
except ValueError:
likelihood, x, y = 0, 0, 0
frames_annotated[label]["x"].append(int(x))
frames_annotated[label]["y"].append(int(y))
frames_annotated[label]["likelihood"].append(likelihood)
if only_first_frame:
break
df = construct_dlc_output_style_df_from_dictionary(marker_predictions=frames_annotated)
df.to_hdf(filepath, "df")
return filepath