Skip to content

Commit 34da7a7

Browse files
authored
Merge pull request #88 from DeepLabCut/multi_scorer_df
Fix loading of data with multiple scorers
2 parents 7c631f6 + e84468e commit 34da7a7

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

src/napari_deeplabcut/_reader.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def read_hdf(filename: str) -> List[LayerData]:
182182
layers = []
183183
for filename in glob.iglob(filename):
184184
temp = pd.read_hdf(filename)
185+
temp = misc.merge_multiple_scorers(temp)
185186
header = misc.DLCHeader(temp.columns)
186187
temp = temp.droplevel("scorer", axis=1)
187188
if "individuals" not in temp.columns.names:

src/napari_deeplabcut/_tests/test_misc.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import numpy as np
12
import os
23
import pandas as pd
34
import pytest
@@ -24,6 +25,31 @@ def test_encode_categories():
2425
inds = misc.encode_categories(categories, return_map=False)
2526

2627

28+
def test_merge_multiple_scorers_no_likelihood(fake_keypoints):
29+
temp = fake_keypoints.copy(deep=True)
30+
temp.columns = temp.columns.set_levels(["you"], level="scorer")
31+
df = fake_keypoints.merge(temp, left_index=True, right_index=True)
32+
df = misc.merge_multiple_scorers(df)
33+
pd.testing.assert_frame_equal(df, fake_keypoints)
34+
35+
36+
def test_merge_multiple_scorers(fake_keypoints):
37+
new_columns = pd.MultiIndex.from_product(
38+
fake_keypoints.columns.levels[:-1] + [["x", "y", "likelihood"]],
39+
names=fake_keypoints.columns.names,
40+
)
41+
fake_keypoints = fake_keypoints.reindex(new_columns, axis=1)
42+
fake_keypoints.loc(axis=1)[:, :, :, "likelihood"] = 1
43+
temp = fake_keypoints.copy(deep=True)
44+
temp.columns = temp.columns.set_levels(["you"], level="scorer")
45+
fake_keypoints.iloc[:5] = np.nan
46+
temp.iloc[5:] = np.nan
47+
df = fake_keypoints.merge(temp, left_index=True, right_index=True)
48+
df = misc.merge_multiple_scorers(df)
49+
pd.testing.assert_index_equal(df.columns, fake_keypoints.columns)
50+
assert not df.isna().any(axis=None)
51+
52+
2753
@pytest.mark.parametrize(
2854
"path",
2955
["/home/to/fake/path", "C:\\Users\\with\\fake\\name"],

src/napari_deeplabcut/misc.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,40 @@ def encode_categories(
2727
return inds
2828

2929

30+
def merge_multiple_scorers(
31+
df: pd.DataFrame,
32+
) -> pd.DataFrame:
33+
n_frames = df.shape[0]
34+
header = DLCHeader(df.columns)
35+
n_scorers = len(header._get_unique("scorer"))
36+
if n_scorers == 1:
37+
return df
38+
39+
if "likelihood" in header.coords:
40+
# Merge annotations from multiple scorers to keep
41+
# detections with highest confidence
42+
data = df.to_numpy().reshape((n_frames, n_scorers, -1, 3))
43+
try:
44+
idx = np.nanargmax(data[..., 2], axis=1)
45+
except ValueError: # All-NaN slice encountered
46+
mask = np.isnan(data[..., 2]).all(axis=1, keepdims=True)
47+
mask = np.broadcast_to(mask[..., None], data.shape)
48+
data[mask] = -1
49+
idx = np.nanargmax(data[..., 2], axis=1)
50+
data[mask] = np.nan
51+
data_best = data[
52+
np.arange(n_frames)[:, None], idx, np.arange(data.shape[2])
53+
].reshape((n_frames, -1))
54+
df = pd.DataFrame(
55+
data_best,
56+
index=df.index,
57+
columns=header.columns[: data_best.shape[1]],
58+
)
59+
else: # Arbitrarily pick data from the first scorer
60+
df = df.loc(axis=1)[: header.scorer]
61+
return df
62+
63+
3064
def to_os_dir_sep(path: str) -> str:
3165
"""
3266
Replace all directory separators in `path` with `os.path.sep`.

0 commit comments

Comments
 (0)