Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 7 additions & 6 deletions deeplabcut/benchmark/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
import json
import os
from collections.abc import Container
from pathlib import Path
from typing import Literal

from deeplabcut.benchmark.base import Benchmark, Result, ResultCollection

DATA_ROOT = os.path.join(os.getcwd(), "data")
CACHE = os.path.join(os.getcwd(), ".results")
DATA_ROOT = Path.cwd() / "data"
CACHE = Path.cwd() / ".results"

__registry = []

Expand Down Expand Up @@ -99,20 +100,20 @@ def evaluate(


def get_filepath(basename: str):
return os.path.join(DATA_ROOT, basename)
return DATA_ROOT / basename


def savecache(results: ResultCollection):
with open(CACHE, "w") as fh:
with Path(CACHE).open("w") as fh:
json.dump(results.todicts(), fh, indent=2)


def loadcache(cache=CACHE, on_missing: Literal["raise", "ignore"] = "ignore") -> ResultCollection:
if not os.path.exists(cache):
if not Path(cache).exists():
if on_missing == "raise":
raise FileNotFoundError(cache)
return ResultCollection()
with open(cache) as fh:
with Path(cache).open() as fh:
try:
data = json.load(fh)
except json.decoder.JSONDecodeError as e:
Expand Down
43 changes: 19 additions & 24 deletions deeplabcut/benchmark/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

"""Evaluation metrics for the DeepLabCut benchmark."""

import os
import pickle
from collections import defaultdict
from pathlib import Path

import numpy as np
import pandas as pd
Expand All @@ -33,7 +33,7 @@ def _format_gt_data(h5file: str, test_indices: list[int] | None = None):
except KeyError:
n_unique = 0
guarantee_multiindex_rows(df)
file_paths = [os.path.join(*row) for row in df.index.to_list()]
file_paths = [Path(*row) for row in df.index.to_list()]
temp = (
df.stack("individuals", dropna=False)
.reindex(animals, level="individuals")
Expand Down Expand Up @@ -98,19 +98,18 @@ def calc_prediction_errors(preds, gt):
return errors


def _map(strings, substrings):
def _map(paths: list[Path], subpaths: list[Path]) -> dict[Path, Path]:
"""Map image paths from predicted data to GT as the first are typically absolute
whereas the latter are relative to the project path."""

lookup = dict()
strings_ = strings.copy()
substrings_ = substrings.copy()
while strings_:
string = strings_.pop()
for s in substrings_:
if string.endswith(s):
lookup[string] = s
substrings_.remove(s)
lookup = {}
paths_ = paths.copy()
subpaths_ = subpaths.copy()
while paths_:
path = paths_.pop()
for s in subpaths_:
if path.parts[-len(s.parts) :] == s.parts:
lookup[path] = s
subpaths_.remove(s)
break
return lookup

Expand Down Expand Up @@ -146,6 +145,7 @@ def calc_map_from_obj(
drop_kpts=None,
):
"""Calculate mean average precision (mAP) based on predictions."""
eval_results = {Path(k): v for k, v in eval_results_obj.items()}
df = pd.read_hdf(h5_file)
try:
df.drop("single", level="individuals", axis=1, inplace=True)
Expand All @@ -157,7 +157,7 @@ def calc_map_from_obj(
test_indices = _load_test_indices(metadata_file)
df_test = df.iloc[test_indices]
test_images = load_test_images(h5_file, metadata_file)
missing_images = set(test_images) - set(eval_results_obj.keys())
missing_images = set(test_images) - set(eval_results)
if len(missing_images) > 0:
raise ValueError(
f"Failed to compute the test mAP: there are test images missing from theprediction object: {missing_images}"
Expand All @@ -184,7 +184,7 @@ def calc_map_from_obj(
for ind in sorted(drop_kpts, reverse=True):
kpts.pop(ind)

assemblies_pred = conv_obj_to_assemblies(eval_results_obj, kpts)
assemblies_pred = conv_obj_to_assemblies(eval_results, kpts)
with deeplabcut.benchmark.utils.DisableOutput():
oks = inferenceutils.evaluate_assembly(
assemblies_pred,
Expand Down Expand Up @@ -213,7 +213,7 @@ def calc_rmse_from_obj(
for ind in sorted(drop_kpts, reverse=True):
kpts.pop(ind)

test_objects = {k: v for k, v in eval_results_obj.items() if k in gt["annotations"].keys()}
test_objects = {Path(k): v for k, v in eval_results_obj.items() if Path(k) in gt["annotations"]}
if len(gt["annotations"]) != len(test_objects):
gt_images = list(gt["annotations"].keys())
missing_images = [img for img in gt_images if img not in test_objects]
Expand All @@ -239,22 +239,17 @@ def calc_rmse_from_obj(
return np.nanmean(errors[..., 0])


def load_test_images(h5file: str, metadata: str) -> list[str]:
def load_test_images(h5file: str, metadata: str) -> list[Path]:
"""Returns the names of the test images for the benchmark, in the order
corresponding to the test indices."""
df = pd.read_hdf(h5file)
test_indices = _load_test_indices(metadata)
df_test = df.iloc[test_indices]
test_images = []
for img_path in df_test.index:
if not isinstance(img_path, str):
img_path = os.path.join(*img_path)
test_images.append(img_path)
return test_images
return [Path(img_path) if isinstance(img_path, str) else Path(*img_path) for img_path in df_test.index]


def _load_test_indices(shuffle_metadata_path: str) -> list[int]:
"""Returns the indices of test images in the training dataset dataframe."""
with open(shuffle_metadata_path, "rb") as f:
with Path(shuffle_metadata_path).open("rb") as f:
test_indices = set([int(i) for i in pickle.load(f)[2]])
return list(sorted(test_indices))
3 changes: 2 additions & 1 deletion deeplabcut/benchmark/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import pkgutil
import sys
from pathlib import Path


class RedirectStdStreams:
Expand Down Expand Up @@ -46,7 +47,7 @@ def __exit__(self, exc_type, exc_value, traceback):

class DisableOutput(RedirectStdStreams):
def __init__(self):
devnull = open(os.devnull, "w")
devnull = Path(os.devnull).open("w")
super().__init__(stdout=devnull, stderr=devnull)


Expand Down
Loading
Loading