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.
Files changed (220) hide show
  1. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/PKG-INFO +12 -12
  2. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/README.md +7 -7
  3. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/pyproject.toml +12 -5
  4. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/mlflow_logging.py +113 -3
  5. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/parallel_workers.py +43 -2
  6. jabs_behavior_classifier-0.46.2/src/jabs/project/pose_attribute_cache.py +88 -0
  7. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project.py +161 -3
  8. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_paths.py +20 -2
  9. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/video_manager.py +18 -5
  10. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/cli-tools.md +24 -9
  11. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/gui.md +2 -1
  12. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/classify.py +9 -4
  13. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/cli.py +13 -4
  14. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/cross_validation.py +6 -0
  15. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/update_labels.py +44 -1
  16. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/update_pose.py +187 -6
  17. jabs_behavior_classifier-0.46.2/src/jabs/scripts/gui_entrypoint.py +131 -0
  18. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/initialize_project.py +10 -6
  19. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/classification_thread.py +9 -1
  20. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/central_widget.py +133 -18
  21. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/main_window.py +11 -0
  22. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/video_list_widget.py +20 -0
  23. jabs_behavior_classifier-0.46.2/src/jabs/video_reader/utilities.py +57 -0
  24. jabs_behavior_classifier-0.46.0/src/jabs/scripts/gui_entrypoint.py +0 -120
  25. jabs_behavior_classifier-0.46.0/src/jabs/video_reader/utilities.py +0 -31
  26. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/LICENSE +0 -0
  27. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/behavior_search/__init__.py +0 -0
  28. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/behavior_search/behavior_search_util.py +0 -0
  29. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/__init__.py +0 -0
  30. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/base.py +0 -0
  31. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/classifier.py +0 -0
  32. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/classifier_utils.py +0 -0
  33. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/cross_validation.py +0 -0
  34. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/factories.py +0 -0
  35. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/multi_class_classifier.py +0 -0
  36. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/protocols.py +0 -0
  37. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/classifier/training_report.py +0 -0
  38. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/__init__.py +0 -0
  39. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/angle_index.py +0 -0
  40. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/__init__.py +0 -0
  41. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/angles.py +0 -0
  42. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/angular_velocity.py +0 -0
  43. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/base_group.py +0 -0
  44. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/centroid_velocity.py +0 -0
  45. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/pairwise_distances.py +0 -0
  46. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/point_speeds.py +0 -0
  47. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/base_features/point_velocities.py +0 -0
  48. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/feature_base_class.py +0 -0
  49. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/feature_group_base_class.py +0 -0
  50. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/features.py +0 -0
  51. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/__init__.py +0 -0
  52. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/corner.py +0 -0
  53. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/food_hopper.py +0 -0
  54. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/landmark_group.py +0 -0
  55. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/landmark_features/lixit.py +0 -0
  56. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/__init__.py +0 -0
  57. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/hu_moments.py +0 -0
  58. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/moment_cache.py +0 -0
  59. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/moments.py +0 -0
  60. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/segment_group.py +0 -0
  61. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/segmentation_features/shape_descriptors.py +0 -0
  62. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/__init__.py +0 -0
  63. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/closest_distances.py +0 -0
  64. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/closest_fov_angles.py +0 -0
  65. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/closest_fov_distances.py +0 -0
  66. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/pairwise_social_distances.py +0 -0
  67. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/social_distance.py +0 -0
  68. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/social_features/social_group.py +0 -0
  69. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/window_operations/__init__.py +0 -0
  70. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/window_operations/signal_stats.py +0 -0
  71. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/feature_extraction/window_operations/window_stats.py +0 -0
  72. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/__init__.py +0 -0
  73. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v2.py +0 -0
  74. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v3.py +0 -0
  75. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v4.py +0 -0
  76. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v5.py +0 -0
  77. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v6.py +0 -0
  78. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v7.py +0 -0
  79. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/pose_estimation/pose_est_v8.py +0 -0
  80. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/__init__.py +0 -0
  81. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/export_training.py +0 -0
  82. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/feature_manager.py +0 -0
  83. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/prediction_manager.py +0 -0
  84. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_merge.py +0 -0
  85. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_pruning.py +0 -0
  86. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/project_utils.py +0 -0
  87. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/read_training.py +0 -0
  88. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/session_tracker.py +0 -0
  89. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/settings_manager.py +0 -0
  90. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/timeline_annotations.py +0 -0
  91. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/track_labels.py +0 -0
  92. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/project/video_labels.py +0 -0
  93. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/__init__.py +0 -0
  94. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/LICENSE +0 -0
  95. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/behavior-search.md +0 -0
  96. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/classifier-types.md +0 -0
  97. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/features.md +0 -0
  98. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/file-formats.md +0 -0
  99. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/StackedImgs.svg +0 -0
  100. {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
  101. {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
  102. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/classifier_controls.png +0 -0
  103. {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
  104. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/identity_gaps.png +0 -0
  105. {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
  106. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/keypoint_legend.png +0 -0
  107. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/label_viz.png +0 -0
  108. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/main_window.png +0 -0
  109. {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
  110. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/pose_overlay.png +0 -0
  111. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/search-dialog.png +0 -0
  112. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/selecting_frames.png +0 -0
  113. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/stacked_timeline.png +0 -0
  114. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/timeline_menu.png +0 -0
  115. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/imgs/track_overlay.png +0 -0
  116. {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
  117. {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
  118. {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
  119. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/keyboard-shortcuts.md +0 -0
  120. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/labeling.md +0 -0
  121. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/multi-class.md +0 -0
  122. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/nwb-export.md +0 -0
  123. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/overview.md +0 -0
  124. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/postprocessing.md +0 -0
  125. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/docs/user_guide/project-setup.md +0 -0
  126. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/eartag_images/s2.svg +0 -0
  127. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/eartag_images/s3.svg +0 -0
  128. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/eartag_images/s4.svg +0 -0
  129. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/fail_whale.png +0 -0
  130. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/resources/icon.png +0 -0
  131. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/schema/__init__.py +0 -0
  132. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/schema/metadata.py +0 -0
  133. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/__init__.py +0 -0
  134. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/__init__.py +0 -0
  135. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/compute_features.py +0 -0
  136. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/convert_parquet.py +0 -0
  137. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/convert_to_nwb.py +0 -0
  138. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/postprocessing.py +0 -0
  139. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/sample_frames.py +0 -0
  140. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/cli/sample_pose_intervals.py +0 -0
  141. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/generate_features.py +0 -0
  142. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/scripts/merge_projects.py +0 -0
  143. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/__init__.py +0 -0
  144. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/__init__.py +0 -0
  145. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/behavior_timeline_widget.py +0 -0
  146. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/frame_ruler_widget.py +0 -0
  147. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/__init__.py +0 -0
  148. {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
  149. {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
  150. {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
  151. {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
  152. {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
  153. {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
  154. {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
  155. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/behavior_timeline/track_widgets/timeline_util.py +0 -0
  156. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/classify_strategy.py +0 -0
  157. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/colors.py +0 -0
  158. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/constants.py +0 -0
  159. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/__init__.py +0 -0
  160. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/about_dialog.py +0 -0
  161. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/annotation_edit_dialog.py +0 -0
  162. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/annotation_info_dialog.py +0 -0
  163. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/archive_behavior_dialog.py +0 -0
  164. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/behavior_search_dialog.py +0 -0
  165. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/license_dialog.py +0 -0
  166. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/message_dialog.py +0 -0
  167. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/progress_dialog.py +0 -0
  168. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/project_pruning_dialog.py +0 -0
  169. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/training_report.py +0 -0
  170. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/update_check_dialog.py +0 -0
  171. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/user_guide_dialog.py +0 -0
  172. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/dialogs/video_info_dialog.py +0 -0
  173. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/ear_tag_icons.py +0 -0
  174. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/exceptions.py +0 -0
  175. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/export_training_thread.py +0 -0
  176. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/__init__.py +0 -0
  177. {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
  178. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/label_count_widget.py +0 -0
  179. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_control_widget/main_control_widget.py +0 -0
  180. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/README.md +0 -0
  181. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/__init__.py +0 -0
  182. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/central_widget_mode.py +0 -0
  183. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/constants.py +0 -0
  184. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/menu_builder.py +0 -0
  185. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/main_window/menu_handlers.py +0 -0
  186. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/__init__.py +0 -0
  187. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/frame_with_overlays.py +0 -0
  188. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/__init__.py +0 -0
  189. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/annotation_overlay.py +0 -0
  190. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/bounding_box.py +0 -0
  191. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/control_overlay.py +0 -0
  192. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/floating_id_overlay.py +0 -0
  193. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/label_overlay.py +0 -0
  194. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/overlay.py +0 -0
  195. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/overlays/pose_overlay.py +0 -0
  196. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/player_thread.py +0 -0
  197. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/player_widget.py +0 -0
  198. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/player_widget/pose_drawing.py +0 -0
  199. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/project_loader_thread.py +0 -0
  200. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/search_bar_widget.py +0 -0
  201. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/README.md +0 -0
  202. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/__init__.py +0 -0
  203. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/cache_format_settings_group.py +0 -0
  204. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/classifier_mode_settings_group.py +0 -0
  205. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/collapsible_section.py +0 -0
  206. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/cross_validation_settings_group.py +0 -0
  207. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/postprocessing_group.py +0 -0
  208. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/postprocessing_settings/__init__.py +0 -0
  209. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/session_tracking_group.py +0 -0
  210. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/settings_dialog.py +0 -0
  211. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/settings_dialog/settings_group.py +0 -0
  212. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/training_strategy.py +0 -0
  213. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/training_thread.py +0 -0
  214. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/ui/util.py +0 -0
  215. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/utils/__init__.py +0 -0
  216. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/utils/update_checker.py +0 -0
  217. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/version/__init__.py +0 -0
  218. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/video_reader/__init__.py +0 -0
  219. {jabs_behavior_classifier-0.46.0 → jabs_behavior_classifier-0.46.2}/src/jabs/video_reader/frame_annotation.py +0 -0
  220. {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.0
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.0
9
- Requires-Dist: jabs-core==0.46.0
10
- Requires-Dist: jabs-io==0.46.0
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>=20.0.0,<23.0.0
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
  [![Python 3.10-3.14](https://img.shields.io/badge/python-3.10--3.14-blue.svg)](https://www.python.org/downloads/)
53
53
  [![PyPI version](https://img.shields.io/pypi/v/jabs-behavior-classifier.svg)](https://pypi.org/project/jabs-behavior-classifier/)
54
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.0/LICENSE)
54
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/LICENSE)
55
55
  [![DOI](https://img.shields.io/badge/DOI-10.7554%2FeLife.107259.2-green.svg)](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.0/docs/user-guide/overview.md) · [Sample Data](https://doi.org/10.5281/zenodo.16697331) · [Contact Us](mailto:jabs@jax.org)
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
- ![JABS Screenshot](https://raw.githubusercontent.com/KumarLabJax/JABS-behavior-classifier/v0.46.0/docs/assets/images/jabs_screenshot.png)
61
+ ![JABS Screenshot](https://raw.githubusercontent.com/KumarLabJax/JABS-behavior-classifier/v0.46.2/docs/assets/images/jabs_screenshot.png)
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.0/vm/README.md)
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.0/docs/user-guide/overview.md)** — Markdown documentation
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.0/CONTRIBUTING.md)
234
- - [Development Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.0/docs/development/development.md)
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
  [![Python 3.10-3.14](https://img.shields.io/badge/python-3.10--3.14-blue.svg)](https://www.python.org/downloads/)
10
10
  [![PyPI version](https://img.shields.io/pypi/v/jabs-behavior-classifier.svg)](https://pypi.org/project/jabs-behavior-classifier/)
11
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.0/LICENSE)
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.2/LICENSE)
12
12
  [![DOI](https://img.shields.io/badge/DOI-10.7554%2FeLife.107259.2-green.svg)](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.0/docs/user-guide/overview.md) · [Sample Data](https://doi.org/10.5281/zenodo.16697331) · [Contact Us](mailto:jabs@jax.org)
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
- ![JABS Screenshot](https://raw.githubusercontent.com/KumarLabJax/JABS-behavior-classifier/v0.46.0/docs/assets/images/jabs_screenshot.png)
18
+ ![JABS Screenshot](https://raw.githubusercontent.com/KumarLabJax/JABS-behavior-classifier/v0.46.2/docs/assets/images/jabs_screenshot.png)
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.0/vm/README.md)
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.0/docs/user-guide/overview.md)** — Markdown documentation
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.0/CONTRIBUTING.md)
191
- - [Development Guide](https://github.com/KumarLabJax/JABS-behavior-classifier/blob/v0.46.0/docs/development/development.md)
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.0"
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.0",
19
- "jabs-core==0.46.0",
20
- "jabs-io==0.46.0",
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>=20.0.0,<23.0.0",
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 as an artifact. Connection config comes
279
- from the environment; ``env_file``, if given, is loaded into it first.
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 get_fps
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 = get_fps(str(job["video_path"]))
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)