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
2 changes: 2 additions & 0 deletions deeplabcut/pose_estimation_pytorch/apis/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ def evaluate_snapshot(
)
scores_filepath = output_filename.with_suffix(".csv")
scores_filepath = scores_filepath.with_stem(scores_filepath.stem + "-results")
print(f"Evaluation results file: {scores_filepath.name}")
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider replacing print statements with a logging framework to allow for configurable and consistent output handling across the codebase.

Suggested change
print(f"Evaluation results file: {scores_filepath.name}")
logging.info(f"Evaluation results file: {scores_filepath.name}")

Copilot uses AI. Check for mistakes.
save_evaluation_results(df_scores, scores_filepath, show_errors, pcutoff)

if per_keypoint_evaluation:
Expand Down Expand Up @@ -829,6 +830,7 @@ def evaluate_network(
snapshot_uid=get_scorer_uid(snapshot, detector_snapshot),
modelprefix=modelprefix,
)
print(f"Evaluation scorer: {scorer}")
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider using logging here instead of print to ensure consistency with other parts of the code that handle error and info messages.

Copilot uses AI. Check for mistakes.
evaluate_snapshot(
loader=loader,
cfg=cfg,
Expand Down
134 changes: 121 additions & 13 deletions deeplabcut/pose_estimation_pytorch/apis/videos.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,58 @@ def analyze_videos(
print(f"Creating a TopDownDynamicCropper with configuration {top_down_dynamic}")
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider replacing print statements with a logging framework to improve error tracking and maintainability in production code.

Suggested change
print(f"Creating a TopDownDynamicCropper with configuration {top_down_dynamic}")
logging.info(f"Creating a TopDownDynamicCropper with configuration {top_down_dynamic}")

Copilot uses AI. Check for mistakes.
dynamic = TopDownDynamicCropper(**top_down_dynamic)

snapshot = utils.get_model_snapshots(
snapshot_index, loader.model_folder, loader.pose_task
)[0]
try:
snapshot = utils.get_model_snapshots(
snapshot_index, loader.model_folder, loader.pose_task
)[0]
except (ValueError, IndexError) as e:
print(f"Error loading snapshot with index {snapshot_index}: {e}")
print("Attempting to find available snapshots...")
Comment on lines +457 to +458
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using print statements for error reporting may limit flexibility; consider using a logging framework that allows configurable log levels and better integration in production.

Suggested change
print(f"Error loading snapshot with index {snapshot_index}: {e}")
print("Attempting to find available snapshots...")
logging.error(f"Error loading snapshot with index {snapshot_index}: {e}")
logging.info("Attempting to find available snapshots...")

Copilot uses AI. Check for mistakes.

# Try to get all available snapshots
try:
all_snapshots = utils.get_model_snapshots("all", loader.model_folder, loader.pose_task)
if all_snapshots:
# Try to find a "best" snapshot first
best_snapshots = [s for s in all_snapshots if s.best]
if best_snapshots:
snapshot = best_snapshots[0]
print(f"Found and using best snapshot: {snapshot.path}")
else:
# Use the last available snapshot
snapshot = all_snapshots[-1]
print(f"No best snapshot found, using last available: {snapshot.path}")
else:
raise FileNotFoundError(f"No snapshots found in {loader.model_folder}")
except Exception as fallback_error:
raise FileNotFoundError(f"Failed to load any snapshots from {loader.model_folder}. Original error: {e}. Fallback error: {fallback_error}")
Comment on lines +452 to +476
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider refactoring the repeated error handling logic for loading snapshots into a helper function to reduce code duplication and improve maintainability.

Suggested change
try:
snapshot = utils.get_model_snapshots(
snapshot_index, loader.model_folder, loader.pose_task
)[0]
except (ValueError, IndexError) as e:
print(f"Error loading snapshot with index {snapshot_index}: {e}")
print("Attempting to find available snapshots...")
# Try to get all available snapshots
try:
all_snapshots = utils.get_model_snapshots("all", loader.model_folder, loader.pose_task)
if all_snapshots:
# Try to find a "best" snapshot first
best_snapshots = [s for s in all_snapshots if s.best]
if best_snapshots:
snapshot = best_snapshots[0]
print(f"Found and using best snapshot: {snapshot.path}")
else:
# Use the last available snapshot
snapshot = all_snapshots[-1]
print(f"No best snapshot found, using last available: {snapshot.path}")
else:
raise FileNotFoundError(f"No snapshots found in {loader.model_folder}")
except Exception as fallback_error:
raise FileNotFoundError(f"Failed to load any snapshots from {loader.model_folder}. Original error: {e}. Fallback error: {fallback_error}")
snapshot = load_snapshot_with_fallback(
snapshot_index, loader.model_folder, loader.pose_task
)

Copilot uses AI. Check for mistakes.

# Additional validation for best snapshots
if "best" in str(snapshot.path) and not snapshot.path.exists():
print(f"Warning: Best snapshot path {snapshot.path} does not exist. Checking for alternative snapshots...")
# Try to find any available snapshot
try:
all_snapshots = utils.get_model_snapshots("all", loader.model_folder, loader.pose_task)
if all_snapshots:
# Try to find a different best snapshot
best_snapshots = [s for s in all_snapshots if s.best and s.path.exists()]
if best_snapshots:
snapshot = best_snapshots[0]
print(f"Using alternative best snapshot: {snapshot.path}")
else:
# Use the last available snapshot
snapshot = all_snapshots[-1]
print(f"Using alternative snapshot: {snapshot.path}")
else:
raise FileNotFoundError(f"No snapshots found in {loader.model_folder}")
except Exception as e:
raise FileNotFoundError(f"Failed to find alternative snapshots: {e}")

# Verify the snapshot file exists
if not snapshot.path.exists():
raise FileNotFoundError(f"Snapshot file not found: {snapshot.path}")

print(f"Successfully loaded snapshot: {snapshot.path}")

# Load the BU model for the conditions provider
cond_provider = None
Expand Down Expand Up @@ -497,9 +546,56 @@ def analyze_videos(
if detector_batch_size is None:
detector_batch_size = loader.project_cfg.get("detector_batch_size", 1)

detector_snapshot = utils.get_model_snapshots(
detector_snapshot_index, loader.model_folder, Task.DETECT
)[0]
try:
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Refactor the duplicated fallback logic for snapshot retrieval into a shared helper function to reduce code duplication and enhance maintainability.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The error handling logic for detector snapshot loading is almost identical to that for the model snapshot; consider abstracting this logic into a shared function to reduce duplication.

Copilot uses AI. Check for mistakes.
detector_snapshot = utils.get_model_snapshots(
detector_snapshot_index, loader.model_folder, Task.DETECT
)[0]
except (ValueError, IndexError) as e:
print(f"Error loading detector snapshot with index {detector_snapshot_index}: {e}")
print("Attempting to find available detector snapshots...")

# Try to get all available detector snapshots
try:
all_detector_snapshots = utils.get_model_snapshots("all", loader.model_folder, Task.DETECT)
if all_detector_snapshots:
# Try to find a "best" detector snapshot first
best_detector_snapshots = [s for s in all_detector_snapshots if s.best]
if best_detector_snapshots:
detector_snapshot = best_detector_snapshots[0]
print(f"Found and using best detector snapshot: {detector_snapshot.path}")
else:
# Use the last available detector snapshot
detector_snapshot = all_detector_snapshots[-1]
print(f"No best detector snapshot found, using last available: {detector_snapshot.path}")
else:
raise FileNotFoundError(f"No detector snapshots found in {loader.model_folder}")
except Exception as fallback_error:
raise FileNotFoundError(f"Failed to load any detector snapshots from {loader.model_folder}. Original error: {e}. Fallback error: {fallback_error}")

# Additional validation for detector snapshots
if "best" in str(detector_snapshot.path) and not detector_snapshot.path.exists():
print(f"Warning: Best detector snapshot path {detector_snapshot.path} does not exist. Checking for alternative detector snapshots...")
try:
all_detector_snapshots = utils.get_model_snapshots("all", loader.model_folder, Task.DETECT)
if all_detector_snapshots:
# Try to find a different best detector snapshot
best_detector_snapshots = [s for s in all_detector_snapshots if s.best and s.path.exists()]
if best_detector_snapshots:
detector_snapshot = best_detector_snapshots[0]
print(f"Using alternative best detector snapshot: {detector_snapshot.path}")
else:
# Use the last available detector snapshot
detector_snapshot = all_detector_snapshots[-1]
print(f"Using alternative detector snapshot: {detector_snapshot.path}")
else:
raise FileNotFoundError(f"No detector snapshots found in {loader.model_folder}")
except Exception as e:
raise FileNotFoundError(f"Failed to find alternative detector snapshots: {e}")

# Verify the detector snapshot file exists
if not detector_snapshot.path.exists():
raise FileNotFoundError(f"Detector snapshot file not found: {detector_snapshot.path}")

print(f" -> Using detector {detector_snapshot.path}")
detector_runner = utils.get_detector_inference_runner(
model_config=loader.model_cfg,
Expand All @@ -509,9 +605,12 @@ def analyze_videos(
)

dlc_scorer = loader.scorer(snapshot, detector_snapshot)
print(f"Using scorer: {dlc_scorer}")

# Reading video and init variables
videos = utils.list_videos_in_folder(videos, videotype, shuffle=in_random_order)
h5_files_created = False # Track if any .h5 files were created

for video in videos:
if destfolder is None:
output_path = video.parent
Expand Down Expand Up @@ -583,6 +682,7 @@ def analyze_videos(
output_prefix=output_prefix,
save_as_csv=save_as_csv,
)
h5_files_created = True # .h5 file was created

if multi_animal:
assemblies_path = output_path / f"{output_prefix}_assemblies.pickle"
Expand Down Expand Up @@ -631,6 +731,7 @@ def analyze_videos(
output_prefix=output_prefix + "_ctd",
save_as_csv=save_as_csv,
)
h5_files_created = True # .h5 file was created for CTD tracking

elif auto_track:
convert_detections2tracklets(
Expand All @@ -654,14 +755,21 @@ def analyze_videos(
destfolder=str(output_path),
save_as_csv=save_as_csv,
)
h5_files_created = True # .h5 file was created by stitch_tracklets

print(
"The videos are analyzed. Now your research can truly start!\n"
"You can create labeled videos with 'create_labeled_video'.\n"
"If the tracking is not satisfactory for some videos, consider expanding the "
"training set. You can use the function 'extract_outlier_frames' to extract a "
"few representative outlier frames.\n"
)
if h5_files_created:
print(
"The videos are analyzed. Now your research can truly start!\n"
"You can create labeled videos with 'create_labeled_video'.\n"
"If the tracking is not satisfactory for some videos, consider expanding the "
"training set. You can use the function 'extract_outlier_frames' to extract a "
"few representative outlier frames.\n"
)
else:
print(
"No .h5 files were created during video analysis. Please check your code and "
"ensure that the video inference and output generation are correct.\n"
)

return dlc_scorer

Expand Down
13 changes: 11 additions & 2 deletions deeplabcut/pose_estimation_pytorch/data/snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,21 @@ class Snapshot:
path: Path

def uid(self) -> str:
return self.path.stem.split("-")[-1]
if self.best:
return f"best-{self.epochs}"
else:
return str(self.epochs)

@staticmethod
def from_path(path: Path) -> "Snapshot":
best = "-best" in path.stem
epochs = int(path.stem.split("-")[-1])
# Use regex to extract epoch number more robustly
match = re.search(r'-(\d+)\.pt$', path.name)
if match:
epochs = int(match.group(1))
else:
# Fallback to original method if regex fails
epochs = int(path.stem.split("-")[-1])
return Snapshot(best=best, epochs=epochs, path=path)


Expand Down
2 changes: 1 addition & 1 deletion deeplabcut/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
# Licensed under GNU Lesser General Public License v3.0
#

__version__ = "3.0.0rc9"
__version__ = "3.0.0rc10"
VERSION = __version__
2 changes: 1 addition & 1 deletion reinstall.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
pip uninstall deeplabcut
python3 setup.py sdist bdist_wheel
pip install dist/deeplabcut-3.0.0rc9-py3-none-any.whl
pip install dist/deeplabcut-3.0.0rc10-py3-none-any.whl
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def pytorch_config_paths() -> list[str]:

setuptools.setup(
name="deeplabcut",
version="3.0.0rc9",
version="3.0.0rc10",
author="A. & M.W. Mathis Labs",
author_email="alexander@deeplabcut.org",
description="Markerless pose-estimation of user-defined features with deep learning",
Expand Down
Loading