jabs-behavior-classifier 0.46.0__tar.gz → 0.46.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/PKG-INFO +12 -12
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/README.md +7 -7
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/pyproject.toml +12 -5
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/mlflow_logging.py +113 -3
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/parallel_workers.py +43 -2
- jabs_behavior_classifier-0.46.2/src/jabs/project/pose_attribute_cache.py +88 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project.py +161 -3
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_paths.py +20 -2
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/video_manager.py +18 -5
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/cli-tools.md +24 -9
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/gui.md +2 -1
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/classify.py +9 -4
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/cli.py +13 -4
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/cross_validation.py +6 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/update_labels.py +44 -1
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/update_pose.py +187 -6
- jabs_behavior_classifier-0.46.2/src/jabs/scripts/gui_entrypoint.py +131 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/initialize_project.py +10 -6
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/classification_thread.py +9 -1
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/central_widget.py +133 -18
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/main_window.py +11 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/video_list_widget.py +20 -0
- jabs_behavior_classifier-0.46.2/src/jabs/video_reader/utilities.py +57 -0
- jabs_behavior_classifier-0.46.0/src/jabs/scripts/gui_entrypoint.py +0 -120
- jabs_behavior_classifier-0.46.0/src/jabs/video_reader/utilities.py +0 -31
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/LICENSE +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/behavior_search/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/behavior_search/behavior_search_util.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/base.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/classifier.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/classifier_utils.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/cross_validation.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/factories.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/multi_class_classifier.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/protocols.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/training_report.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/angle_index.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/angles.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/angular_velocity.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/base_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/centroid_velocity.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/pairwise_distances.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/point_speeds.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/point_velocities.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/feature_base_class.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/feature_group_base_class.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/features.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/corner.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/food_hopper.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/landmark_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/lixit.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/hu_moments.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/moment_cache.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/moments.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/segment_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/shape_descriptors.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/closest_distances.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/closest_fov_angles.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/closest_fov_distances.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/pairwise_social_distances.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/social_distance.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/social_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/window_operations/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/window_operations/signal_stats.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/window_operations/window_stats.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v2.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v3.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v4.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v5.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v6.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v7.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v8.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/export_training.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/feature_manager.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/prediction_manager.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_merge.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_pruning.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_utils.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/read_training.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/session_tracker.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/settings_manager.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/timeline_annotations.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/track_labels.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/video_labels.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/LICENSE +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/behavior-search.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/classifier-types.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/features.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/file-formats.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/StackedImgs.svg +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/bbox-identity-overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/centroid-identity-overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/classifier_controls.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/floating-identity-overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/identity_gaps.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/identity_gaps_with_label.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/keypoint_legend.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/label_viz.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/main_window.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/minimalist-identity-overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/pose_overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/search-dialog.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/selecting_frames.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/stacked_timeline.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/timeline_menu.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/track_overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/video-context-menu.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/video-control-overlay.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/video-control-overlay.svg +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/keyboard-shortcuts.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/labeling.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/multi-class.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/nwb-export.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/overview.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/postprocessing.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/project-setup.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/eartag_images/s2.svg +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/eartag_images/s3.svg +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/eartag_images/s4.svg +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/fail_whale.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/icon.png +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/schema/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/schema/metadata.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/compute_features.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/convert_parquet.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/convert_to_nwb.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/postprocessing.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/sample_frames.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/sample_pose_intervals.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/generate_features.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/merge_projects.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/behavior_timeline_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/frame_ruler_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/behavior_probability_detail_bar.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/class_prediction_detail_bar.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/label_detail_bar.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/label_overview_bar.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/label_track_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/per_class_prediction_track_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/prediction_track_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/timeline_util.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/classify_strategy.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/colors.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/constants.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/about_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/annotation_edit_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/annotation_info_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/archive_behavior_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/behavior_search_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/license_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/message_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/progress_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/project_pruning_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/training_report.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/update_check_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/user_guide_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/video_info_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/ear_tag_icons.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/exceptions.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/export_training_thread.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/k_fold_slider_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/label_count_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/main_control_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/README.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/central_widget_mode.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/constants.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/menu_builder.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/menu_handlers.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/frame_with_overlays.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/annotation_overlay.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/bounding_box.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/control_overlay.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/floating_id_overlay.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/label_overlay.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/overlay.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/pose_overlay.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/player_thread.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/player_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/pose_drawing.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/project_loader_thread.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/search_bar_widget.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/README.md +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/cache_format_settings_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/classifier_mode_settings_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/collapsible_section.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/cross_validation_settings_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/postprocessing_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/postprocessing_settings/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/session_tracking_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/settings_dialog.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/settings_group.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/training_strategy.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/training_thread.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/util.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/utils/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/utils/update_checker.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/version/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/video_reader/__init__.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/video_reader/frame_annotation.py +0 -0
- {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/video_reader/video_reader.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jabs-behavior-classifier
|
|
3
|
-
Version: 0.46.
|
|
3
|
+
Version: 0.46.2
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Glen Beane, Brian Geuther, Keith Sheppard, Alexander Berger-Liedtka
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
License-File: LICENSE
|
|
8
|
-
Requires-Dist: jabs-behavior==0.46.
|
|
9
|
-
Requires-Dist: jabs-core==0.46.
|
|
10
|
-
Requires-Dist: jabs-io==0.46.
|
|
8
|
+
Requires-Dist: jabs-behavior==0.46.2
|
|
9
|
+
Requires-Dist: jabs-core==0.46.2
|
|
10
|
+
Requires-Dist: jabs-io==0.46.2
|
|
11
11
|
Requires-Dist: h5py>=3.10.0,<4.0.0
|
|
12
12
|
Requires-Dist: markdown2>=2.5.1,<3.0.0
|
|
13
13
|
Requires-Dist: numpy>=2.0.0,<3.0.0
|
|
@@ -20,7 +20,7 @@ Requires-Dist: shapely>=2.0.1,<3.0.0
|
|
|
20
20
|
Requires-Dist: tabulate>=0.9.0,<1.0.0
|
|
21
21
|
Requires-Dist: toml>=0.10.2,<0.11.0
|
|
22
22
|
Requires-Dist: xgboost>=2.0.0,<3.0.0
|
|
23
|
-
Requires-Dist: pyarrow>=
|
|
23
|
+
Requires-Dist: pyarrow>=23.0.1,<24.0.0
|
|
24
24
|
Requires-Dist: rich>=14.0.0,<15.0.0
|
|
25
25
|
Requires-Dist: rich-argparse>=1.7.1,<2.0.0
|
|
26
26
|
Requires-Dist: intervaltree>=3.1.0,<4.0.0
|
|
@@ -51,14 +51,14 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
|
|
52
52
|
[](https://www.python.org/downloads/)
|
|
53
53
|
[](https://pypi.org/project/jabs-behavior-classifier/)
|
|
54
|
-
[](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
54
|
+
[](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/LICENSE)
|
|
55
55
|
[](https://doi.org/10.7554/eLife.107259.2)
|
|
56
56
|
|
|
57
|
-
[Documentation](https://jabs-tutorial.readthedocs.io/) · [User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
57
|
+
[Documentation](https://jabs-tutorial.readthedocs.io/) · [User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/docs/user-guide/overview.md) · [Sample Data](https://doi.org/10.5281/zenodo.16697331) · [Contact Us](mailto:jabs@jax.org)
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
61
|
-

|
|
62
62
|
|
|
63
63
|
</div>
|
|
64
64
|
|
|
@@ -188,7 +188,7 @@ JABS requires pose files generated from the Kumar Lab's mouse pose estimation ne
|
|
|
188
188
|
|
|
189
189
|
We provide Singularity/Apptainer definition files and SLURM batch scripts for running
|
|
190
190
|
JABS on Linux compute clusters. See
|
|
191
|
-
[vm/README.md](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
191
|
+
[vm/README.md](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/vm/README.md)
|
|
192
192
|
for build instructions and usage details.
|
|
193
193
|
|
|
194
194
|
## Project Portability
|
|
@@ -224,14 +224,14 @@ JABS uses four version numbers to track compatibility:
|
|
|
224
224
|
## Documentation
|
|
225
225
|
|
|
226
226
|
- **[ReadTheDocs Tutorial](https://jabs-tutorial.readthedocs.io/)** — Complete user guide and tutorials
|
|
227
|
-
- **[User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
227
|
+
- **[User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/docs/user-guide/overview.md)** — Markdown documentation
|
|
228
228
|
- **[Sample Data](https://doi.org/10.5281/zenodo.16697331)** — Test datasets for demonstration
|
|
229
229
|
|
|
230
230
|
## Contributing
|
|
231
231
|
|
|
232
232
|
Interested in contributing? Check out our:
|
|
233
|
-
- [Contributing Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
234
|
-
- [Development Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
233
|
+
- [Contributing Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/CONTRIBUTING.md)
|
|
234
|
+
- [Development Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/docs/development/development.md)
|
|
235
235
|
|
|
236
236
|
## Citation
|
|
237
237
|
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
|
|
9
9
|
[](https://www.python.org/downloads/)
|
|
10
10
|
[](https://pypi.org/project/jabs-behavior-classifier/)
|
|
11
|
-
[](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
11
|
+
[](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/LICENSE)
|
|
12
12
|
[](https://doi.org/10.7554/eLife.107259.2)
|
|
13
13
|
|
|
14
|
-
[Documentation](https://jabs-tutorial.readthedocs.io/) · [User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
14
|
+
[Documentation](https://jabs-tutorial.readthedocs.io/) · [User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/docs/user-guide/overview.md) · [Sample Data](https://doi.org/10.5281/zenodo.16697331) · [Contact Us](mailto:jabs@jax.org)
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
-

|
|
19
19
|
|
|
20
20
|
</div>
|
|
21
21
|
|
|
@@ -145,7 +145,7 @@ JABS requires pose files generated from the Kumar Lab's mouse pose estimation ne
|
|
|
145
145
|
|
|
146
146
|
We provide Singularity/Apptainer definition files and SLURM batch scripts for running
|
|
147
147
|
JABS on Linux compute clusters. See
|
|
148
|
-
[vm/README.md](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
148
|
+
[vm/README.md](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/vm/README.md)
|
|
149
149
|
for build instructions and usage details.
|
|
150
150
|
|
|
151
151
|
## Project Portability
|
|
@@ -181,14 +181,14 @@ JABS uses four version numbers to track compatibility:
|
|
|
181
181
|
## Documentation
|
|
182
182
|
|
|
183
183
|
- **[ReadTheDocs Tutorial](https://jabs-tutorial.readthedocs.io/)** — Complete user guide and tutorials
|
|
184
|
-
- **[User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
184
|
+
- **[User Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/docs/user-guide/overview.md)** — Markdown documentation
|
|
185
185
|
- **[Sample Data](https://doi.org/10.5281/zenodo.16697331)** — Test datasets for demonstration
|
|
186
186
|
|
|
187
187
|
## Contributing
|
|
188
188
|
|
|
189
189
|
Interested in contributing? Check out our:
|
|
190
|
-
- [Contributing Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
191
|
-
- [Development Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.
|
|
190
|
+
- [Contributing Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/CONTRIBUTING.md)
|
|
191
|
+
- [Development Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/docs/development/development.md)
|
|
192
192
|
|
|
193
193
|
## Citation
|
|
194
194
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "jabs-behavior-classifier"
|
|
3
|
-
version = "0.46.
|
|
3
|
+
version = "0.46.2"
|
|
4
4
|
description = ""
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10,<3.15"
|
|
@@ -15,9 +15,9 @@ authors = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
dependencies = [
|
|
18
|
-
"jabs-behavior==0.46.
|
|
19
|
-
"jabs-core==0.46.
|
|
20
|
-
"jabs-io==0.46.
|
|
18
|
+
"jabs-behavior==0.46.2",
|
|
19
|
+
"jabs-core==0.46.2",
|
|
20
|
+
"jabs-io==0.46.2",
|
|
21
21
|
"h5py>=3.10.0,<4.0.0",
|
|
22
22
|
"markdown2>=2.5.1,<3.0.0",
|
|
23
23
|
"numpy>=2.0.0,<3.0.0",
|
|
@@ -30,7 +30,7 @@ dependencies = [
|
|
|
30
30
|
"tabulate>=0.9.0,<1.0.0",
|
|
31
31
|
"toml>=0.10.2,<0.11.0",
|
|
32
32
|
"xgboost>=2.0.0,<3.0.0",
|
|
33
|
-
"pyarrow>=
|
|
33
|
+
"pyarrow>=23.0.1,<24.0.0",
|
|
34
34
|
"rich>=14.0.0,<15.0.0",
|
|
35
35
|
"rich-argparse>=1.7.1,<2.0.0",
|
|
36
36
|
"intervaltree>=3.1.0,<4.0.0",
|
|
@@ -47,6 +47,13 @@ nwb = ["jabs-io[nwb]"]
|
|
|
47
47
|
yaml = ["pyyaml>=6.0.0"]
|
|
48
48
|
mlflow = ["mlflow>=3.8.1"]
|
|
49
49
|
|
|
50
|
+
[tool.uv]
|
|
51
|
+
# Security floors for transitive dependencies (Dependabot). These packages are
|
|
52
|
+
# pulled indirectly (e.g. cryptography via the optional `mlflow` extra ->
|
|
53
|
+
# google-auth); constraining them here bumps the locked version past known
|
|
54
|
+
# vulnerabilities without adding them as direct dependencies.
|
|
55
|
+
constraint-dependencies = ["cryptography>=48.0.1"]
|
|
56
|
+
|
|
50
57
|
[tool.uv.sources]
|
|
51
58
|
jabs-behavior = { workspace = true }
|
|
52
59
|
jabs-io = { workspace = true }
|
|
@@ -25,6 +25,8 @@ import logging
|
|
|
25
25
|
import math
|
|
26
26
|
import os
|
|
27
27
|
import subprocess
|
|
28
|
+
import tempfile
|
|
29
|
+
import zipfile
|
|
28
30
|
from pathlib import Path
|
|
29
31
|
from typing import TYPE_CHECKING
|
|
30
32
|
|
|
@@ -37,6 +39,10 @@ if TYPE_CHECKING:
|
|
|
37
39
|
|
|
38
40
|
logger = logging.getLogger(__name__)
|
|
39
41
|
|
|
42
|
+
#: File name used for the zipped annotations artifact attached to each run. Kept
|
|
43
|
+
#: constant across runs so the artifact is easy to find and compare in the UI.
|
|
44
|
+
ANNOTATIONS_ARCHIVE_NAME = "annotations.zip"
|
|
45
|
+
|
|
40
46
|
|
|
41
47
|
def mlflow_available() -> bool:
|
|
42
48
|
"""Return True if an import spec for the optional ``mlflow`` package is found.
|
|
@@ -130,6 +136,83 @@ def load_env_file(env_file: Path | None, *, override: bool = True) -> dict[str,
|
|
|
130
136
|
return values
|
|
131
137
|
|
|
132
138
|
|
|
139
|
+
def archive_annotations(annotations_dir: Path, output_path: Path) -> Path | None:
|
|
140
|
+
"""Zip a JABS project's annotations directory for upload as an MLflow artifact.
|
|
141
|
+
|
|
142
|
+
Captures the label set the cross-validation run was computed from, so a run's
|
|
143
|
+
metrics can be traced back to (and reproduced from) the exact annotations.
|
|
144
|
+
|
|
145
|
+
Archive members are stored under a single top-level directory named after
|
|
146
|
+
``annotations_dir`` (normally ``annotations/``), so unpacking the zip recreates
|
|
147
|
+
the directory rather than scattering JSON files into the current directory.
|
|
148
|
+
Files are added in sorted order to keep archives reproducible.
|
|
149
|
+
|
|
150
|
+
Symlinks are **not** archived, and neither are files reached through one. The
|
|
151
|
+
archive is uploaded to a tracking server, so it must contain only what really
|
|
152
|
+
lives in the project: zipping a symlink stores the *target's* content, which
|
|
153
|
+
would publish files from outside the project. Skipped symlinks are logged.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
annotations_dir: The project's ``jabs/annotations`` directory.
|
|
157
|
+
output_path: Destination path for the ``.zip`` file.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
``output_path`` if an archive was written, or None if ``annotations_dir``
|
|
161
|
+
does not exist or holds no archivable files -- there is then nothing worth
|
|
162
|
+
uploading.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
OSError: If reading the annotations or writing the archive fails.
|
|
166
|
+
"""
|
|
167
|
+
if not annotations_dir.is_dir():
|
|
168
|
+
logger.warning(
|
|
169
|
+
"Annotations directory not found, no annotations artifact: %s", annotations_dir
|
|
170
|
+
)
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
real_root = annotations_dir.resolve()
|
|
174
|
+
files: list[Path] = []
|
|
175
|
+
skipped_links = 0
|
|
176
|
+
for path in sorted(annotations_dir.rglob("*")):
|
|
177
|
+
# Skip symlinks themselves, and any entry whose real location falls
|
|
178
|
+
# outside the annotations directory -- the latter guards against a
|
|
179
|
+
# directory symlink being traversed (which pathlib does not currently do,
|
|
180
|
+
# but is not contractually guaranteed across Python versions).
|
|
181
|
+
if path.is_symlink() or not path.resolve().is_relative_to(real_root):
|
|
182
|
+
skipped_links += 1
|
|
183
|
+
continue
|
|
184
|
+
if path.is_file():
|
|
185
|
+
files.append(path)
|
|
186
|
+
|
|
187
|
+
if skipped_links:
|
|
188
|
+
logger.warning(
|
|
189
|
+
"Skipped %d symlinked entr%s under %s: symlinks are not archived",
|
|
190
|
+
skipped_links,
|
|
191
|
+
"y" if skipped_links == 1 else "ies",
|
|
192
|
+
annotations_dir,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
if not files:
|
|
196
|
+
logger.warning(
|
|
197
|
+
"No archivable annotation files in %s, no annotations artifact", annotations_dir
|
|
198
|
+
)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
root = Path(annotations_dir.name)
|
|
202
|
+
with zipfile.ZipFile(output_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
|
|
203
|
+
for path in files:
|
|
204
|
+
archive.write(path, arcname=str(root / path.relative_to(annotations_dir)))
|
|
205
|
+
|
|
206
|
+
logger.info(
|
|
207
|
+
"Archived %d annotation file(s) from %s into %s (%d bytes)",
|
|
208
|
+
len(files),
|
|
209
|
+
annotations_dir,
|
|
210
|
+
output_path.name,
|
|
211
|
+
output_path.stat().st_size,
|
|
212
|
+
)
|
|
213
|
+
return output_path
|
|
214
|
+
|
|
215
|
+
|
|
133
216
|
def _git_sha() -> str | None:
|
|
134
217
|
"""Short git SHA of the jabs checkout, or None if unavailable."""
|
|
135
218
|
try:
|
|
@@ -266,23 +349,29 @@ def log_cross_validation_to_mlflow(
|
|
|
266
349
|
*,
|
|
267
350
|
report_data: TrainingReportData,
|
|
268
351
|
report_file: Path | None = None,
|
|
352
|
+
annotations_dir: Path | None = None,
|
|
269
353
|
env_file: Path | None = None,
|
|
270
354
|
experiment_name: str | None = None,
|
|
271
355
|
run_name: str | None = None,
|
|
272
356
|
tags: dict[str, str] | None = None,
|
|
273
357
|
log_report_artifact: bool = True,
|
|
358
|
+
log_annotations_artifact: bool = True,
|
|
274
359
|
) -> tuple[str, str]:
|
|
275
360
|
"""Create one MLflow run for a cross-validation run and return its ids.
|
|
276
361
|
|
|
277
362
|
Logs aggregate CV metrics, curated params, auto-derived plus caller tags,
|
|
278
|
-
and (optionally) the training report
|
|
279
|
-
from the environment; ``env_file``, if
|
|
363
|
+
and (optionally) the training report and a zip of the project's annotations
|
|
364
|
+
as artifacts. Connection config comes from the environment; ``env_file``, if
|
|
365
|
+
given, is loaded into it first.
|
|
280
366
|
|
|
281
367
|
Args:
|
|
282
368
|
report_data: Completed training report data.
|
|
283
369
|
report_file: Path to the saved training report to upload as an artifact.
|
|
284
370
|
Ignored if None or missing on disk, or if ``log_report_artifact`` is
|
|
285
371
|
False.
|
|
372
|
+
annotations_dir: The project's ``jabs/annotations`` directory, zipped and
|
|
373
|
+
uploaded as ``annotations.zip``. Ignored if None, if the directory is
|
|
374
|
+
missing or empty, or if ``log_annotations_artifact`` is False.
|
|
286
375
|
env_file: Optional ``.env`` file with ``MLFLOW_*`` connection settings.
|
|
287
376
|
If None, connection config comes from the ambient environment.
|
|
288
377
|
experiment_name: Explicit MLflow experiment name. If None, the experiment is
|
|
@@ -294,12 +383,15 @@ def log_cross_validation_to_mlflow(
|
|
|
294
383
|
tags: Caller-supplied run tags; merged over the auto-derived tags (so a
|
|
295
384
|
user tag with the same key wins).
|
|
296
385
|
log_report_artifact: Whether to upload the training report artifact.
|
|
386
|
+
log_annotations_artifact: Whether to upload the zipped annotations artifact.
|
|
297
387
|
|
|
298
388
|
Returns:
|
|
299
389
|
A ``(run_id, tracking_uri)`` tuple for the created MLflow run.
|
|
300
390
|
|
|
301
391
|
Raises:
|
|
302
|
-
MlflowLoggingError: If the ``mlflow`` package is not installed.
|
|
392
|
+
MlflowLoggingError: If the ``mlflow`` package is not installed. A failure
|
|
393
|
+
to archive the annotations is *not* fatal: it is logged as a warning
|
|
394
|
+
and the run keeps its metrics, params, and report artifact.
|
|
303
395
|
"""
|
|
304
396
|
try:
|
|
305
397
|
import mlflow
|
|
@@ -340,6 +432,24 @@ def log_cross_validation_to_mlflow(
|
|
|
340
432
|
if log_report_artifact and report_file is not None and Path(report_file).is_file():
|
|
341
433
|
mlflow.log_artifact(str(report_file))
|
|
342
434
|
|
|
435
|
+
if log_annotations_artifact and annotations_dir is not None:
|
|
436
|
+
# The annotations are supplementary provenance, so a failure to
|
|
437
|
+
# archive them must not cost the run its metrics, params, or report.
|
|
438
|
+
try:
|
|
439
|
+
with tempfile.TemporaryDirectory() as staging_dir:
|
|
440
|
+
archive = archive_annotations(
|
|
441
|
+
Path(annotations_dir), Path(staging_dir) / ANNOTATIONS_ARCHIVE_NAME
|
|
442
|
+
)
|
|
443
|
+
if archive is not None:
|
|
444
|
+
mlflow.log_artifact(str(archive))
|
|
445
|
+
except OSError:
|
|
446
|
+
logger.warning(
|
|
447
|
+
"Failed to archive annotations from %s; run %s has no annotations artifact",
|
|
448
|
+
annotations_dir,
|
|
449
|
+
run.info.run_id,
|
|
450
|
+
exc_info=True,
|
|
451
|
+
)
|
|
452
|
+
|
|
343
453
|
run_id = run.info.run_id
|
|
344
454
|
|
|
345
455
|
tracking_uri = mlflow.get_tracking_uri()
|
|
@@ -21,7 +21,7 @@ from jabs.core.constants import MULTICLASS_NONE_BEHAVIOR
|
|
|
21
21
|
from jabs.core.enums import CacheFormat
|
|
22
22
|
from jabs.pose_estimation import open_pose_file
|
|
23
23
|
from jabs.video_reader import VideoReader
|
|
24
|
-
from jabs.video_reader.utilities import
|
|
24
|
+
from jabs.video_reader.utilities import get_fps_and_nframes
|
|
25
25
|
|
|
26
26
|
from .track_labels import TrackLabels
|
|
27
27
|
from .video_labels import VideoLabels
|
|
@@ -32,6 +32,21 @@ if TYPE_CHECKING:
|
|
|
32
32
|
from jabs.pose_estimation import PoseEstimation
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def preload_worker_modules() -> None:
|
|
36
|
+
"""Process-pool initializer that warms each worker's import cache.
|
|
37
|
+
|
|
38
|
+
Intended to be passed as the ``initializer`` for the application
|
|
39
|
+
``ProcessPoolManager``. A worker must import this module to resolve this
|
|
40
|
+
reference, and importing it (with its transitive dependencies) pulls in the
|
|
41
|
+
heavy stack workers need: numpy, pandas and h5py directly, plus
|
|
42
|
+
``jabs.feature_extraction`` (which brings in scipy) and the pose/video
|
|
43
|
+
readers (which bring in OpenCV). Doing that once at pool warm-up means the
|
|
44
|
+
first project-load scan and the first training run don't pay the import
|
|
45
|
+
cost. The function body is intentionally empty; the import triggered by
|
|
46
|
+
resolving it is the whole point.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
|
|
35
50
|
class _BaseFeatureLoadJobSpec(TypedDict):
|
|
36
51
|
"""Shared fields for binary and multi-class feature-load jobs."""
|
|
37
52
|
|
|
@@ -212,13 +227,39 @@ def _apply_macos_fork_lapack_workaround() -> None:
|
|
|
212
227
|
fe.feature_base_class._use_numpy_detrend = True
|
|
213
228
|
|
|
214
229
|
|
|
230
|
+
def _warn_on_frame_count_mismatch(
|
|
231
|
+
video: str, video_frame_count: int, pose_frame_count: int
|
|
232
|
+
) -> None:
|
|
233
|
+
"""Log a warning when a video and its pose file disagree on frame count.
|
|
234
|
+
|
|
235
|
+
The video/pose frame-count check is deferred from project load. Feature
|
|
236
|
+
extraction and predictions are indexed to pose frames, so a mismatch does
|
|
237
|
+
not corrupt them; this surfaces the discrepancy for headless/batch runs
|
|
238
|
+
where no video is opened interactively.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
video: Video filename, used in the log message.
|
|
242
|
+
video_frame_count: Frame count read from the video file.
|
|
243
|
+
pose_frame_count: Frame count from the pose file.
|
|
244
|
+
"""
|
|
245
|
+
if video_frame_count != pose_frame_count:
|
|
246
|
+
logger.warning(
|
|
247
|
+
"%s: video frame count (%d) does not match pose file frame count (%d); "
|
|
248
|
+
"features and predictions are pose-indexed and unaffected",
|
|
249
|
+
video,
|
|
250
|
+
video_frame_count,
|
|
251
|
+
pose_frame_count,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
215
255
|
def _open_pose_and_labels(
|
|
216
256
|
job: _BaseFeatureLoadJobSpec,
|
|
217
257
|
) -> "tuple[PoseEstimation, VideoLabels | None, float]":
|
|
218
258
|
"""Set up the macOS workaround and open pose + label resources for a job."""
|
|
219
259
|
_apply_macos_fork_lapack_workaround()
|
|
220
260
|
pose_est = open_pose_file(job["pose_path"], job["cache_dir"])
|
|
221
|
-
fps =
|
|
261
|
+
fps, video_frame_count = get_fps_and_nframes(str(job["video_path"]))
|
|
262
|
+
_warn_on_frame_count_mismatch(job["video"], video_frame_count, pose_est.num_frames)
|
|
222
263
|
labels_obj = _load_video_labels(job["annotations_path"], pose_est)
|
|
223
264
|
return pose_est, labels_obj, fps
|
|
224
265
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Persistent cache of per-video pose attributes to skip the load-time pose scan.
|
|
2
|
+
|
|
3
|
+
The project-load scan opens every pose HDF5 file to read a handful of small,
|
|
4
|
+
intrinsic attributes (frame count, identity count, static objects, lixit
|
|
5
|
+
keypoint count, cm-per-pixel flag). Those values change only when the pose file
|
|
6
|
+
itself changes, so they are cached in ``jabs/cache/pose_attribute_cache.json``
|
|
7
|
+
keyed by video filename and gated by a cheap ``stat`` token of the pose file. A
|
|
8
|
+
subsequent load then rescans only the videos whose pose file is new or changed.
|
|
9
|
+
|
|
10
|
+
The cache is an optimization only: a missing, unreadable, or schema-mismatched
|
|
11
|
+
cache simply triggers a full rescan, and a failed write is logged and ignored.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Bump when the cached entry schema changes so stale caches are ignored wholesale.
|
|
21
|
+
SCHEMA_VERSION = 1
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def pose_token(pose_path: Path) -> str:
|
|
25
|
+
"""Return a cheap change-detection token for a pose file.
|
|
26
|
+
|
|
27
|
+
Uses a single ``stat`` (size and modification time) rather than opening or
|
|
28
|
+
hashing the file, so computing the token stays far cheaper than the scan it
|
|
29
|
+
guards.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
pose_path: Path to the pose HDF5 file.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A ``"<size>:<mtime_ns>"`` token string.
|
|
36
|
+
"""
|
|
37
|
+
st = pose_path.stat()
|
|
38
|
+
return f"{st.st_size}:{st.st_mtime_ns}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load(cache_path: Path | None) -> dict[str, dict]:
|
|
42
|
+
"""Load the per-video attribute map from the cache file.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
cache_path: Path to the cache JSON file, or ``None`` when caching is
|
|
46
|
+
disabled (``use_cache=False``).
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Mapping of video filename to its cached entry. Returns an empty mapping
|
|
50
|
+
when caching is disabled, or the file is missing, unreadable, or written
|
|
51
|
+
by a different schema version.
|
|
52
|
+
"""
|
|
53
|
+
if cache_path is None or not cache_path.exists():
|
|
54
|
+
return {}
|
|
55
|
+
try:
|
|
56
|
+
with cache_path.open("r") as f:
|
|
57
|
+
data = json.load(f)
|
|
58
|
+
except (OSError, ValueError):
|
|
59
|
+
logger.warning("Could not read pose attribute cache %s; rescanning", cache_path)
|
|
60
|
+
return {}
|
|
61
|
+
if not isinstance(data, dict) or data.get("schema_version") != SCHEMA_VERSION:
|
|
62
|
+
return {}
|
|
63
|
+
videos = data.get("videos")
|
|
64
|
+
return videos if isinstance(videos, dict) else {}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def save(cache_path: Path | None, videos: dict[str, dict]) -> None:
|
|
68
|
+
"""Atomically write the per-video attribute map to the cache file.
|
|
69
|
+
|
|
70
|
+
A write failure is logged and swallowed: the cache is an optimization, so
|
|
71
|
+
failing to persist it must never break project loading.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
cache_path: Path to the cache JSON file, or ``None`` when caching is
|
|
75
|
+
disabled (in which case this is a no-op).
|
|
76
|
+
videos: Mapping of video filename to its cached entry.
|
|
77
|
+
"""
|
|
78
|
+
if cache_path is None:
|
|
79
|
+
return
|
|
80
|
+
payload = {"schema_version": SCHEMA_VERSION, "videos": videos}
|
|
81
|
+
tmp = cache_path.with_suffix(".json.tmp")
|
|
82
|
+
try:
|
|
83
|
+
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
with tmp.open("w") as f:
|
|
85
|
+
json.dump(payload, f, indent=2, sort_keys=True)
|
|
86
|
+
tmp.replace(cache_path)
|
|
87
|
+
except OSError:
|
|
88
|
+
logger.warning("Could not write pose attribute cache %s", cache_path, exc_info=True)
|