Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/jabs/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""JABS Behavior Classifier"""
"""JABS Behavior classifier"""
4 changes: 0 additions & 4 deletions src/jabs/__main__.py

This file was deleted.

4 changes: 2 additions & 2 deletions src/jabs/classifier/classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
)
from sklearn.model_selection import LeaveOneGroupOut

from jabs.project import Project, TrackLabels, load_training_data
from jabs.types import (
from jabs.enums import (
DEFAULT_CV_GROUPING_STRATEGY,
ClassifierType,
CrossValidationGroupingStrategy,
)
from jabs.project import Project, TrackLabels, load_training_data
from jabs.utils import hash_file

_VERSION = 10
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/classifier/training_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import numpy as np
from tabulate import tabulate

from jabs.types import CrossValidationGroupingStrategy
from jabs.enums import CrossValidationGroupingStrategy


@dataclass
Expand Down
10 changes: 6 additions & 4 deletions src/jabs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
APP_NAME = "JABS"
APP_NAME_LONG = f"{ORG_NAME} Animal Behavior System"

# maximum number of recent projects to show in the File->Recent Projects menu
RECENT_PROJECTS_MAX = 10
# a hard coded random seed used for the final training
# This is not used during cross-validation, but to ensure that final classifier is reproducible
# we use this fixed seed when training the final model after cross validation.
FINAL_TRAIN_SEED = 0xAB3BDB

# some defaults for compressing hdf5 output
COMPRESSION = "gzip"
COMPRESSION_OPTS_DEFAULT = 6

# settings keys
SETTINGS_CV_GROUPING = "cv_grouping"
# settings keys for project settings stored in the project.json file
CV_GROUPING_KEY = "cv_grouping"
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions src/jabs/project/export_training.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

import jabs.feature_extraction
import jabs.version
from jabs.constants import FINAL_TRAIN_SEED
from jabs.project.project_utils import to_safe_name
from jabs.utils import FINAL_TRAIN_SEED

# these are used for type hints, but cause circular imports
# TYPE_CHECKING is always false at runtime, so this gets around that
# also requires enclosing Project and Classifier type hints in quotes
if TYPE_CHECKING:
from jabs.enums import ClassifierType
from jabs.project import Project
from jabs.types import ClassifierType


def export_training_data(
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/project/feature_manager.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import jabs.feature_extraction as feature_extraction
from jabs.enums import ProjectDistanceUnit
from jabs.pose_estimation import (
PoseEstimation,
get_points_per_lixit,
get_pose_file_major_version,
get_pose_path,
get_static_objects_in_file,
)
from jabs.types import ProjectDistanceUnit

from .project_paths import ProjectPaths
from .video_manager import VideoManager
Expand Down
12 changes: 2 additions & 10 deletions src/jabs/project/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
import pandas as pd

import jabs.feature_extraction as fe
from jabs.enums import CrossValidationGroupingStrategy, ProjectDistanceUnit
from jabs.pose_estimation import PoseEstimation, get_pose_path, open_pose_file
from jabs.types import CrossValidationGroupingStrategy, ProjectDistanceUnit

from .feature_manager import FeatureManager
from .parallel_workers import FeatureLoadJobSpec, collect_labeled_features
Expand Down Expand Up @@ -104,14 +104,6 @@ def __init__(
# the session tracker.
self._session_tracker.start_session()

def _ensure_executor(self) -> "ProcessPoolManager | None":
"""Return the shared application-level ProcessPoolManager, or None if running single-threaded."""
return self._process_pool

def shutdown_executor(self) -> None:
"""No-op: executor is owned by the application, not individual projects."""
pass

def _validate_pose_files(self):
"""Ensure all videos have corresponding pose files."""
err = False
Expand Down Expand Up @@ -619,7 +611,7 @@ def get_labeled_features(
}
jobs.append(job)

executor = self._ensure_executor()
executor = self._process_pool
results_by_video: dict[str, dict] = {}

if executor is not None:
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/project/read_training.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np
import pandas as pd

from jabs.types import ClassifierType, ProjectDistanceUnit
from jabs.enums import ClassifierType, ProjectDistanceUnit


def read_project_settings(h5_file: h5py.Group) -> dict:
Expand Down
6 changes: 3 additions & 3 deletions src/jabs/project/settings_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import typing

import jabs.feature_extraction as feature_extraction
from jabs.constants import SETTINGS_CV_GROUPING
from jabs.types.cv_grouping import DEFAULT_CV_GROUPING_STRATEGY, CrossValidationGroupingStrategy
from jabs.constants import CV_GROUPING_KEY
from jabs.enums.cv_grouping import DEFAULT_CV_GROUPING_STRATEGY, CrossValidationGroupingStrategy
from jabs.version import version_str

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -93,7 +93,7 @@ def cv_grouping_strategy(self) -> CrossValidationGroupingStrategy:
CrossValidationGroupingStrategy: The CV grouping strategy.
"""
grouping_str = self._project_info.get("settings", {}).get(
SETTINGS_CV_GROUPING, DEFAULT_CV_GROUPING_STRATEGY.value
CV_GROUPING_KEY, DEFAULT_CV_GROUPING_STRATEGY.value
)
try:
return CrossValidationGroupingStrategy(grouping_str)
Expand Down
14 changes: 11 additions & 3 deletions src/jabs/resources/docs/user_guide/gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ Clicking the Brightness or Contrast controls will reset the brightness or contra
## Menu

- **JABS→About:** Display About Dialog
- **JABS→Project Settings:** Display Project Settings Dialog
- **JABS→User Guide:** Display User Guide
- **JABS→JABS Preferences:** Open JABS Preferences Dialog
- **JABS→Project Settings:** Open Project Settings Dialog
- **JABS→User Guide:** Open User Guide
- **JABS→Check for Updates:** Check PyPI for JABS updates
- **JABS→View License Agreement:** Display License Agreement
- **JABS→Enable Session Tracking:** Enable labeling session tracking
- **JABS→Quit JABS:** Quit Program
- **File→Open Project:** Select a project directory to open. If a project is already opened, it will be closed and the newly selected project will be opened.
- **File→Open Recent:** Submenu to open recently opened projects.
Expand Down Expand Up @@ -125,10 +125,18 @@ Clicking the Brightness or Contrast controls will reset the brightness or contra
- **Window→Bring All to Front:** Bring all JABS windows to the front
- The Window menu also displays a list of all open JABS windows (main window, user guide, training reports, etc.) with a checkmark (✓) next to the currently active window. Click any window in the list to activate and bring it to the front.

## JABS Preferences Dialog
The **JABS Preferences Dialog**, available from the JABS menu, allows you to configure application-wide settings. These settings affect behavior of the JABS application and are not specific to any single project. These preferences are saved across application restarts, and apply regardless of which project is opened.

Preferences are designed to be easily discoverable; preferences are grouped into logical sections, and each section includes built-in documentation, making it easy to understand the purpose and effect of each option directly within the dialog. Users should explore this dialog and customize preferences as needed.


## Project Settings Dialog

The **Project Settings Dialog**, available from the JABS menu, allows you to configure project-wide settings. Settings are designed to be easily discoverable; each settings group includes built-in documentation, making it easy to understand the purpose and effect of each option directly within the dialog. Users should explore this dialog and customize project settings as needed.

Project settings are saved within the project directory and apply only to the currently opened project.

### Settings Overview

| Setting | Description |
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/scripts/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from rich.console import Console

from jabs.classifier import Classifier
from jabs.enums import ClassifierType
from jabs.project import Project, export_training_data, get_videos_to_prune
from jabs.types import ClassifierType

# find out which classifiers are supported in this environment
CLASSIFIER_CHOICES: list[ClassifierType] = Classifier().classifier_choices()
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/scripts/generate_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import sys
from pathlib import Path

from jabs.enums import ProjectDistanceUnit
from jabs.feature_extraction.features import IdentityFeatures
from jabs.pose_estimation import open_pose_file
from jabs.project import Project
from jabs.types import ProjectDistanceUnit


def generate_feature_cache(args):
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/scripts/initialize_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
import jabs.feature_extraction
import jabs.pose_estimation
import jabs.project
from jabs.enums import ProjectDistanceUnit
from jabs.project.video_manager import VideoManager
from jabs.schema.metadata import validate_metadata
from jabs.types import ProjectDistanceUnit
from jabs.video_reader import VideoReader

DEFAULT_WINDOW_SIZE = 5
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/scripts/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from tabulate import tabulate

from jabs.classifier import Classifier
from jabs.enums import ProjectDistanceUnit
from jabs.project import load_training_data
from jabs.scripts.classify import train
from jabs.types import ProjectDistanceUnit


def main():
Expand Down
6 changes: 6 additions & 0 deletions src/jabs/ui/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# these are settings keys stored in QSettings for the JABS GUI
LICENSE_ACCEPTED_KEY = "license_accepted"
LICENSE_VERSION_KEY = "license_version"
RECENT_PROJECTS_KEY = "recent_projects"
SESSION_TRACKING_ENABLED_KEY = "session_tracking_enabled"
WINDOW_SIZE_KEY = "main_window_size"
32 changes: 21 additions & 11 deletions src/jabs/ui/main_control_widget/main_control_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from PySide6.QtGui import QIcon, QPainter, QPixmap

from jabs.classifier import Classifier
from jabs.types import ClassifierType
from jabs.enums import ClassifierType

from ..colors import (
BEHAVIOR_BUTTON_COLOR_BRIGHT,
Expand Down Expand Up @@ -154,16 +154,7 @@ def __init__(self, *args, **kwargs):
# drop down to select type of classifier to use
self._classifier_selection = QtWidgets.QComboBox()
self._classifier_selection.currentIndexChanged.connect(self.classifier_changed)

classifier_types = Classifier().classifier_choices()
for classifier, name in classifier_types.items():
self._classifier_selection.addItem(name, userData=classifier)

# Set default classifier as the initial selection
for i in range(self._classifier_selection.count()):
if self._classifier_selection.itemData(i) == DEFAULT_CLASSIFIER:
self._classifier_selection.setCurrentIndex(i)
break
self.initialize_classifier_choices()

# slider to set number of times to train/test
self._kslider = KFoldSliderWidget()
Expand Down Expand Up @@ -545,6 +536,25 @@ def set_window_size(self, size: int):
self._add_window_size(size)
self._window_size.setCurrentText(str(size))

def initialize_classifier_choices(self) -> None:
"""populate the classifier selection combobox with available classifiers"""
# first, save the current selection so we can restore it after repopulating
current_classifier = self.classifier_type if self.classifier_type else DEFAULT_CLASSIFIER

# clear and repopulate the classifier selection combobox
self._classifier_selection.blockSignals(True)
self._classifier_selection.clear()
classifier_types = Classifier().classifier_choices()
for classifier, name in classifier_types.items():
self._classifier_selection.addItem(name, userData=classifier)

# now restore the previous selection if possible
for i in range(self._classifier_selection.count()):
if self._classifier_selection.itemData(i) == current_classifier:
self._classifier_selection.setCurrentIndex(i)
break
self._classifier_selection.blockSignals(False)

def remove_behavior(self, behavior: str):
"""remove a behavior from the behavior selection box"""
idx = self.behavior_selection.findText(behavior, QtCore.Qt.MatchFlag.MatchExactly)
Expand Down
2 changes: 1 addition & 1 deletion src/jabs/ui/main_window/central_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
import jabs.feature_extraction
from jabs.behavior_search import SearchHit
from jabs.classifier import Classifier
from jabs.enums import ClassifierType
from jabs.pose_estimation import PoseEstimation, PoseEstimationV8
from jabs.project import Project, TimelineAnnotations, TrackLabels, VideoLabels
from jabs.types import ClassifierType

from ..classification_thread import ClassifyThread
from ..dialogs import AnnotationEditDialog, TrainingReportDialog
Expand Down
10 changes: 3 additions & 7 deletions src/jabs/ui/main_window/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@

from jabs.utils import get_bool_env_var

# maximum number of recent projects to show in the File->Recent Projects menu
RECENT_PROJECTS_MAX = 10

# Environment variable flags
USE_NATIVE_FILE_DIALOG = get_bool_env_var("JABS_NATIVE_FILE_DIALOG", True)

# Settings keys
RECENT_PROJECTS_KEY = "recent_projects"
LICENSE_ACCEPTED_KEY = "license_accepted"
LICENSE_VERSION_KEY = "license_version"
WINDOW_SIZE_KEY = "main_window_size"
SESSION_TRACKING_ENABLED_KEY = "session_tracking_enabled"

# Default window dimensions
DEFAULT_WINDOW_WIDTH = 1280
DEFAULT_WINDOW_HEIGHT = 720
Loading