Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow max tracking args for Kalman filter #1986

Merged

Conversation

eberrigan
Copy link
Contributor

@eberrigan eberrigan commented Oct 8, 2024

Description

Replicating the Error

  • target_instance_count not present in GUI but is present as argument in make_tracker_by_name

    sleap/sleap/nn/tracking.py

    Lines 836 to 872 in 1339f0d

    @classmethod
    def make_tracker_by_name(
    cls,
    # Tracker options
    tracker: str = "flow",
    similarity: str = "instance",
    match: str = "greedy",
    track_window: int = 5,
    robust: float = 1.0,
    min_new_track_points: int = 0,
    min_match_points: int = 0,
    # Optical flow options
    img_scale: float = 1.0,
    of_window_size: int = 21,
    of_max_levels: int = 3,
    save_shifted_instances: bool = False,
    # Pre-tracking options to cull instances
    target_instance_count: int = 0,
    pre_cull_to_target: bool = False,
    pre_cull_iou_threshold: Optional[float] = None,
    # Post-tracking options to connect broken tracks
    post_connect_single_breaks: bool = False,
    # TODO: deprecate these post-tracking cleaning options
    clean_instance_count: int = 0,
    clean_iou_threshold: Optional[float] = None,
    # Kalman filter options
    kf_init_frame_count: int = 0,
    kf_node_indices: Optional[list] = None,
    # Max tracking options
    max_tracks: Optional[int] = None,
    max_tracking: bool = False,
    # Object keypoint similarity options
    oks_errors: Optional[list] = None,
    oks_score_weighting: bool = False,
    oks_normalization: str = "all",
    **kwargs,
    ) -> BaseTracker:

    and is required for the Kalman Filter classmethod

    sleap/sleap/nn/tracking.py

    Lines 1331 to 1338 in 47a0efa

    @classmethod
    def make_tracker(
    cls,
    init_tracker: Optional[Tracker],
    node_indices: List[int],
    instance_count: int,
    instance_iou_threshold: float = 0.8,
    init_frame_count: int = 10,
    so that when the Kalman filter is used from the GUI, the following error always occurs: "Kalman filter requires target instance count.".
Software versions:
SLEAP: 1.4.1a2
TensorFlow: 2.9.2
Numpy: 1.22.4
Python: 3.9.20
OS: macOS-14.5-arm64-arm-64bit
.
.
.
Using already trained model for centroid: /Users/elizabethberrigan/repos/sleap/tests/data/models/minimal_instance.UNet.centroid/training_config.json
Using already trained model for centered_instance: /Users/elizabethberrigan/repos/sleap/tests/data/models/minimal_instance.UNet.centered_instance/training_config.json
Command line call:
sleap-track /Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.slp --only-labeled-frames -m /Users/elizabethberrigan/repos/sleap/tests/data/models/minimal_instance.UNet.centroid/training_config.json -m /Users/elizabethberrigan/repos/sleap/tests/data/models/minimal_instance.UNet.centered_instance/training_config.json --batch_size 4 --tracking.tracker flow --tracking.similarity instance --tracking.match greedy --tracking.track_window 5 --tracking.kf_init_frame_count 10 --tracking.kf_node_indices 0,1,2 --tracking.post_connect_single_breaks 1 --controller_port 9000 --publish_port 9001 -o /Users/elizabethberrigan/repos/sleap/tests/data/tracks/predictions/clip.2node.slp.241003_221743.predictions.slp --verbosity json --no-empty-frames

Started inference at: 2024-10-03 22:17:47.149775
Args:
{
│   'data_path': '/Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.slp',
│   'models': [
│   │   '/Users/elizabethberrigan/repos/sleap/tests/data/models/minimal_instance.UNet.centroid/training_config.json',
│   │   '/Users/elizabethberrigan/repos/sleap/tests/data/models/minimal_instance.UNet.centered_instance/training_config.json'
│   ],
│   'frames': '',
│   'only_labeled_frames': True,
│   'only_suggested_frames': False,
Traceback (most recent call last):
  File "/Users/elizabethberrigan/miniforge3/envs/sleap_v1.4.1a2/bin/sleap-track", line 33, in <module>
    sys.exit(load_entry_point('sleap==1.4.1a2', 'console_scripts', 'sleap-track')())
  File "/Users/elizabethberrigan/miniforge3/envs/sleap_v1.4.1a2/lib/python3.9/site-packages/sleap/nn/inference.py", line 5467, in main
    tracker = _make_tracker_from_cli(args)
  File "/Users/elizabethberrigan/miniforge3/envs/sleap_v1.4.1a2/lib/python3.9/site-packages/sleap/nn/inference.py", line 5401, in _make_tracker_from_cli
    tracker = Tracker.make_tracker_by_name(**policy_args["tracking"])
  File "/Users/elizabethberrigan/miniforge3/envs/sleap_v1.4.1a2/lib/python3.9/site-packages/sleap/nn/tracking.py", line 916, in make_tracker_by_name
    raise ValueError("Kalman filter requires target instance count.")
ValueError: Kalman filter requires target instance count.
│   'output': '/Users/elizabethberrigan/repos/sleap/tests/data/tracks/predictions/clip.2node.slp.241003_221743.predictions.slp',
│   'no_empty_frames': True,
│   'verbosity': 'json',
│   'video.dataset': None,
│   'video.input_format': 'channels_last',
│   'video.index': '',
│   'cpu': False,
Process return code: 1
  • Same error when max_tracking is set

    (sleap_v1.4.1a2) C:\repos\sleap>sleap-label
    Saving config: C:\Users\eb/.sleap/1.4.1a2/preferences.yaml
    
    Software versions:
    SLEAP: 1.4.1a2
    TensorFlow: 2.7.0
    Numpy: 1.21.6
    Python: 3.7.12
    OS: Windows-10-10.0.22621-SP0
    .
    .
    .
    
    Using already trained model for centroid: C:/repos/sleap/tests/data/models/minimal_instance.UNet.centroid/training_config.json
    Using already trained model for centered_instance: C:/repos/sleap/tests/data/models/minimal_instance.UNet.centered_instance/training_config.json
    Command line call:
    sleap-track C:/repos/sleap/tests/data/tracks/clip.2node.slp --video.index 0 --frames 1499 -m C:/repos/sleap/tests/data/models/minimal_instance.UNet.centroid/training_config.json -m C:/repos/sleap/tests/data/models/minimal_instance.UNet.centered_instance/training_config.json --batch_size 4 --tracking.tracker flowmaxtracks --max_instances 2 --tracking.max_tracks 2 --tracking.similarity instance --tracking.match greedy --tracking.track_window 5 --tracking.kf_init_frame_count 10 --tracking.kf_node_indices 0,1 --tracking.post_connect_single_breaks 1 --controller_port 9000 --publish_port 9001 --tracking.max_tracking 1 -o C:/repos/sleap/tests/data/tracks\predictions\clip.2node.slp.241004_091302.predictions.slp --verbosity json --no-empty-frames
    
    Started inference at: 2024-10-04 09:13:06.788183
    Args:
    {
        'data_path': 'C:/repos/sleap/tests/data/tracks/clip.2node.slp',
        'models': [
            'C:/repos/sleap/tests/data/models/minimal_instance.UNet.centroid/training_config.json',
            'C:/repos/sleap/tests/data/models/minimal_instance.UNet.centered_instance/training_config.json'
        ],
        'frames': '1499',
        'only_labeled_frames': False,
        'only_suggested_frames': False,
        'output': 'C:/repos/sleap/tests/data/tracks\\predictions\\clip.2node.slp.241004_091302.predictions.slp',
        'no_empty_frames': True,
        'verbosity': 'json',
        'video.dataset': None,
        'video.input_format': 'channels_last',
        'video.index': '0',
        'cpu': False,
        'first_gpu': False,
        'last_gpu': False,
        'gpu': 'auto',
        'max_edge_length_ratio': 0.25,
        'dist_penalty_weight': 1.0,
        'batch_size': 4,
        'open_in_gui': False,
        'peak_threshold': 0.2,
        'max_instances': 2,
        'tracking.tracker': 'flowmaxtracks',
        'tracking.max_tracking': True,
        'tracking.max_tracks': 2,
        'tracking.target_instance_count': None,
        'tracking.pre_cull_to_target': None,
        'tracking.pre_cull_iou_threshold': None,
        'tracking.post_connect_single_breaks': 1,
        'tracking.clean_instance_count': None,
        'tracking.clean_iou_threshold': None,
        'tracking.similarity': 'instance',
        'tracking.match': 'greedy',
        'tracking.robust': None,
        'tracking.track_window': 5,
        'tracking.min_new_track_points': None,
    Traceback (most recent call last):
      File "C:\Users\eb\Miniforge3\envs\sleap_v1.4.1a2\Scripts\sleap-track-script.py", line 33, in <module>
        sys.exit(load_entry_point('sleap==1.4.1a2', 'console_scripts', 'sleap-track')())
      File "C:\Users\eb\Miniforge3\envs\sleap_v1.4.1a2\lib\site-packages\sleap\nn\inference.py", line 5467, in main
        tracker = _make_tracker_from_cli(args)
      File "C:\Users\eb\Miniforge3\envs\sleap_v1.4.1a2\lib\site-packages\sleap\nn\inference.py", line 5401, in _make_tracker_from_cli
        tracker = Tracker.make_tracker_by_name(**policy_args["tracking"])
      File "C:\Users\eb\Miniforge3\envs\sleap_v1.4.1a2\lib\site-packages\sleap\nn\tracking.py", line 916, in make_tracker_by_name
        raise ValueError("Kalman filter requires target instance count.")
    ValueError: Kalman filter requires target instance count.
        'tracking.min_match_points': None,
        'tracking.img_scale': None,
        'tracking.of_window_size': None,
        'tracking.of_max_levels': None,
        'tracking.save_shifted_instances': None,
        'tracking.kf_node_indices': [0, 1],
        'tracking.kf_init_frame_count': 10
    Process return code: 1
  • max_tracks is configurable from the GUI.

Tracing the Error

  • Tracker.make_tracker_by_name does take target_instance_count as an argument and it is set to 0 by default.

  • kf_init_frame_count is set to 10 in the GUI when people want to use the Kalman filter, while target_instance_count remains 0, producing the error "Kalman filter requires target instance count."

    sleap/sleap/nn/tracking.py

    Lines 957 to 958 in 1339f0d

    elif kf_init_frame_count and not target_instance_count:
    raise ValueError("Kalman filter requires target instance count.")

    • We want it to run these lines instead, which requires target_instance_count and kf_init_frame_count

      sleap/sleap/nn/tracking.py

      Lines 947 to 954 in 1339f0d

      if target_instance_count and kf_init_frame_count:
      kalman_obj = KalmanTracker.make_tracker(
      init_tracker=tracker_obj,
      init_frame_count=kf_init_frame_count,
      node_indices=kf_node_indices,
      instance_count=target_instance_count,
      instance_iou_threshold=pre_cull_iou_threshold,
      )
  • Called here (args parsed from CLI):

    sleap/sleap/nn/tracking.py

    Lines 1564 to 1570 in 1339f0d

    Tracker.add_cli_parser_args(parser)
    args = parser.parse_args()
    tracker_args = {key: val for key, val in vars(args).items() if val is not None}
    tracker = Tracker.make_tracker_by_name(**tracker_args)

  • presence of max_tracking does change the tracker candidate. If people want to keep the exact same behavior as they have now, they will have to use the CLI to set the target_instance_count without setting max_tracks.

  • Related: If we want to cull, we need to add pre_cull_to_target option to the GUI. Right now, nothing will lead to the pre-cull function through the GUI

    sleap/sleap/nn/tracking.py

    Lines 922 to 930 in 1339f0d

    pre_cull_function = None
    if target_instance_count and pre_cull_to_target:
    def pre_cull_function(inst_list):
    cull_frame_instances(
    inst_list,
    instance_count=target_instance_count,
    iou_threshold=pre_cull_iou_threshold,
    )

  • Getting the following error for flowmaxtracks only (not simplemaxtracks).

  • This is not being caught in our tests.

  • More tests need to be added since we didn't catch the args for using the Kalman filter not to be present in the GUI.

  • Codecov results from first push

Using already trained model for centroid: C:/repos/sleap/tests/data/tracks\models\241007_145636.centroid.n=1500\training_config.json
Using already trained model for centered_instance: C:/repos/sleap/tests/data/tracks\models\241007_154651.centered_instance.n=1500\training_config.json
Command line call:
sleap-track C:/repos/sleap/tests/data/tracks/clip.2node.slp --video.index 0 --frames 627,-682 -m C:/repos/sleap/tests/data/tracks\models\241007_145636.centroid.n=1500\training_config.json -m C:/repos/sleap/tests/data/tracks\models\241007_154651.centered_instance.n=1500\training_config.json --batch_size 4 --tracking.tracker flowmaxtracks --tracking.max_tracks 2 --tracking.similarity instance --tracking.match hungarian --tracking.track_window 5 --tracking.kf_init_frame_count 10 --tracking.kf_node_indices 0,1 --tracking.oks_errors  --tracking.oks_score_weighting 0 --tracking.post_connect_single_breaks 1 --controller_port 9000 --publish_port 9001 --tracking.max_tracking 1 -o C:/repos/sleap/tests/data/tracks\predictions\clip.2node.slp.241007_205530.predictions.slp --verbosity json --no-empty-frames

Started inference at: 2024-10-07 20:55:35.535014
Args:
{
    'data_path': 'C:/repos/sleap/tests/data/tracks/clip.2node.slp',
    'models': [
        'C:/repos/sleap/tests/data/tracks\\models\\241007_145636.centroid.n=1500\\training_config.json',
        'C:/repos/sleap/tests/data/tracks\\models\\241007_154651.centered_instance.n=1500\\training_config.json'
    ],
    'frames': '627,-682',
    'only_labeled_frames': False,
    'only_suggested_frames': False,
    'output': 'C:/repos/sleap/tests/data/tracks\\predictions\\clip.2node.slp.241007_205530.predictions.slp',
    'no_empty_frames': True,
    'verbosity': 'json',
    'video.dataset': None,
    'video.input_format': 'channels_last',
    'video.index': '0',
    'cpu': False,
    'first_gpu': False,
    'last_gpu': False,
    'gpu': 'auto',
    'max_edge_length_ratio': 0.25,
    'dist_penalty_weight': 1.0,
    'batch_size': 4,
    'open_in_gui': False,
    'peak_threshold': 0.2,
    'max_instances': None,
    'tracking.tracker': 'flowmaxtracks',
    'tracking.max_tracking': True,
    'tracking.max_tracks': 2,
    'tracking.target_instance_count': None,
    'tracking.pre_cull_to_target': None,
    'tracking.pre_cull_iou_threshold': None,
2024-10-07 20:55:37.291379: I tensorflow/core/platform/cpu_feature_guard.cc:151] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX AVX2
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
    'tracking.post_connect_single_breaks': 1,
    'tracking.clean_instance_count': None,
    'tracking.clean_iou_threshold': None,
    'tracking.similarity': 'instance',
    'tracking.match': 'hungarian',
    'tracking.robust': None,
    'tracking.track_window': 5,
    'tracking.min_new_track_points': None,
    'tracking.min_match_points': None,
2024-10-07 20:55:37.799106: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1525] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 5957 MB memory:  -> device: 0, name: NVIDIA GeForce RTX 2070 SUPER, pci bus id: 0000:01:00.0, compute capability: 7.5
    'tracking.img_scale': None,
    'tracking.of_window_size': None,
    'tracking.of_max_levels': None,
    'tracking.save_shifted_instances': None,
    'tracking.kf_node_indices': [0, 1],
    'tracking.kf_init_frame_count': 10,
    'tracking.oks_errors': None,
    'tracking.oks_score_weighting': 0,
    'tracking.oks_normalization': None
}

INFO:sleap.nn.inference:Auto-selected GPU 0 with 6744 MiB of free memory.
2024-10-07 20:55:43.684523: I tensorflow/stream_executor/cuda/cuda_dnn.cc:366] Loaded cuDNN version 8201
Versions:
Exception in thread Thread-3:
Traceback (most recent call last):
  File "C:\Users\eb\Miniforge3\envs\sleap_dev\lib\threading.py", line 926, in _bootstrap_inner
    self.run()
  File "C:\Users\eb\Miniforge3\envs\sleap_dev\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "c:\repos\sleap\sleap\nn\inference.py", line 2627, in _object_builder
    t=frame_ind,
  File "c:\repos\sleap\sleap\nn\tracking.py", line 1433, in track
    tracked_instances = self.init_tracker.track(untracked_instances, img, t)
  File "c:\repos\sleap\sleap\nn\tracking.py", line 714, in track
    img=img,
  File "c:\repos\sleap\sleap\nn\tracking.py", line 434, in get_candidates
    ref_instances, ref_img, ref_t, img, t
  File "c:\repos\sleap\sleap\nn\tracking.py", line 191, in get_shifted_instances
    max_levels=self.of_max_levels,
  File "c:\repos\sleap\sleap\nn\tracking.py", line 330, in flow_shift_instances
    0.01,
cv2.error: OpenCV(4.6.0) :-1: error: (-5:Bad argument) in function 'calcOpticalFlowPyrLK'
> Overload resolution failed:
>  - prevImg is not a numpy array, neither a scalar
>  - Expected Ptr<cv::UMat> for argument 'prevImg'
  • flow trackers and trackers using the normalized_instance result in the error where the image is an integer type and not a numpy array. This happens because of
    tracked_instances = self.init_tracker.track(untracked_instances, img, t)
    where the init_tracker is flow, flowmaxtracks
    • We can see the Kalman tracker expects untracked_instances and frame index but not image. For these trackers to work we would have to pass the frame index to the Kalman tracker and the image to the initial tracker

      sleap/sleap/nn/tracking.py

      Lines 1465 to 1467 in 15deb3f

      tracked_instances = self.kalman_tracker.track_frame(
      untracked_instances, frame_idx=t
      )
    • Since we are considering phasing the Kalman filter out all together, we are not extending support for the Kalman filter and instead only allowing the Kalman filter with the simple tracking methods and without normalized instances.

Types of changes

  • Bugfix
  • New feature
  • Refactor / Code style update (no logical changes)
  • Build / CI changes
  • Documentation Update
  • Other (explain)

Does this address any currently open issues?

#1447
#1583, #1980

Outside contributors checklist

  • Review the guidelines for contributing to this repository
  • Read and sign the CLA and add yourself to the authors list
  • Make sure you are making a pull request against the develop branch (not main). Also you should start your branch off develop
  • Add tests that prove your fix is effective or that your feature works
  • Add necessary documentation (if appropriate)

Thank you for contributing to SLEAP!

❤️

Summary by CodeRabbit

  • New Features

    • Introduced a new max_tracking option to limit the number of active tracks in the tracking system.
    • Added a main entry point for the inference script, enabling command-line execution and enhanced usability.
    • Updated CLI documentation with new optional arguments for instance cleaning and added an example for the Kalman tracker.
  • Bug Fixes

    • Enhanced logic in the tracking system to handle deprecation of target_instance_count.
  • Tests

    • Added a new parameterized test for the Kalman tracker to ensure compatibility and expected behavior.
  • Documentation

    • Improved clarity and structure of the command-line interface documentation.

Copy link

coderabbitai bot commented Oct 8, 2024

Walkthrough

The pull request introduces enhancements to SLEAP's tracking and inference capabilities. Key modifications include adding a max_tracking feature in the tracking module, updating the Kalman tracker's matching logic, creating a new main entry point for inference, and expanding test coverage for tracking scenarios. The changes focus on providing more flexible track management, improving inference usability, and ensuring robust testing of tracking functionalities across different configurations.

Changes

File Change Summary
sleap/nn/tracking.py - Added max_tracking boolean property to Tracker class
- Marked target_instance_count for deprecation
- Updated track and final_pass methods to handle max tracks
- Modified make_tracker_by_name method
sleap/nn/tracker/kalman.py - Changed valid_match_mask data type from np.bool to bool
sleap/nn/inference.py - Added main() function as CLI entry point
- Implemented argument parsing and inference workflow
tests/nn/test_tracking_integration.py - Added test_kalman_tracker function
- Introduced parameterized tests for tracking scenarios
- Added pytest import
docs/guides/cli.md - Updated CLI documentation with new tracking arguments
- Added Kalman tracker configuration example

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI
    participant Tracker
    participant Inference
    
    User->>CLI: Specify tracking parameters
    CLI->>Tracker: Initialize with max_tracks
    Tracker->>Inference: Process tracks
    Inference-->>Tracker: Limit track generation
    Tracker-->>User: Return tracked results
Loading

Poem

🐰 Tracking rabbits, swift and light,
Max tracks dancing in digital sight,
Kalman's filter, precise and neat,
Inference leaping to a new beat!
Code hops forward with playful might 🏃‍♂️


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (2)
sleap/nn/tracking.py (2)

969-970: Clarify the exception message for missing required parameters.

The ValueError message could be more explicit by naming the parameters:

raise ValueError("Kalman filter requires `max_tracks` or `target_instance_count` to be set.")

This helps users understand which parameters need to be provided.


1403-1403: Remove unused **kwargs parameter from the track method.

The track method includes **kwargs in its signature, but kwargs is not used within the method. Consider removing it to simplify the method and prevent confusion.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fff8761 and 4a8db98.

📒 Files selected for processing (1)
  • sleap/nn/tracking.py (9 hunks)
🧰 Additional context used

@@ -574,7 +574,7 @@ class Tracker(BaseTracker):
max_tracking: bool = False # To enable maximum tracking.

cleaner: Optional[Callable] = None # TODO: deprecate
target_instance_count: int = 0
target_instance_count: int = 0 # TODO: deprecate
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor to remove dependency on deprecated target_instance_count.

The attribute target_instance_count is marked for deprecation but is still being defined and used. Consider refactoring the code to eliminate its usage and replace it with max_tracks wherever applicable to clean up the codebase and avoid confusion.

Comment on lines 827 to 833
elif (self.target_instance_count or self.max_tracks) and self.post_connect_single_breaks:
if not self.target_instance_count:
# If target_instance_count is not set, use max_tracks instead
# target_instance_count not available in the GUI
self.target_instance_count = self.max_tracks
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using deprecated target_instance_count in logic.

The code assigns self.target_instance_count from self.max_tracks, despite target_instance_count being marked for deprecation. Refactor the logic to use self.max_tracks directly, removing reliance on the deprecated attribute.

@@ -850,7 +855,7 @@
of_max_levels: int = 3,
save_shifted_instances: bool = False,
# Pre-tracking options to cull instances
target_instance_count: int = 0,
target_instance_count: int = 0, # TODO: deprecate target_instance_count
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove deprecated parameter target_instance_count from method signature.

The parameter target_instance_count in the make_tracker_by_name method is marked for deprecation but remains in the signature. To streamline the API and prevent future confusion, consider removing it and updating any dependent code to use max_tracks.

@@ -940,11 +946,17 @@
pre_cull_function=pre_cull_function,
max_tracking=max_tracking,
max_tracks=max_tracks,
target_instance_count=target_instance_count,
target_instance_count=target_instance_count, # TODO: deprecate target_instance_count
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Eliminate passing deprecated target_instance_count to the constructor.

When creating tracker_obj, the deprecated target_instance_count is still being passed. Update the constructor and related logic to rely solely on max_tracks, removing the need for the deprecated parameter.

Comment on lines 953 to 977
# Kalman filter requires deprecated target_instance_count
if (max_tracks or target_instance_count) and kf_init_frame_count:
if not target_instance_count:
# If target_instance_count is not set, use max_tracks instead
# target_instance_count not available in the GUI
target_instance_count = max_tracks
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Update Kalman filter implementation to use max_tracks instead of deprecated target_instance_count.

The Kalman filter initialization depends on target_instance_count, which is marked for deprecation. Modify the Kalman filter code to use max_tracks exclusively, ensuring future maintainability and consistency.

@@ -1369,6 +1381,8 @@
if init_tracker.pre_cull_function is None:
init_tracker.pre_cull_function = cull_function

print(f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters.")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix syntax error due to extra parenthesis in print statement.

There is an extra closing parenthesis at the end of the print statement:

print(f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters.)"

Remove the extra parenthesis to correct the syntax error.

Apply this diff:

-print(f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters.)"
+print(f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters.")

connect_single_track_breaks(frames, self.target_instance_count)
print("Connecting single track breaks.")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use logging instead of print statements for better practice.

The print statement for status messages can be replaced with the logging module to provide configurable logging levels and outputs.

Apply this diff:

+import logging
...
+logger = logging.getLogger(__name__)
...
-print("Connecting single track breaks.")
+logger.info("Connecting single track breaks.")

Committable suggestion was skipped due to low confidence.

@@ -1420,7 +1435,7 @@
# Initialize the Kalman filters
self.kalman_tracker.init_filters(self.init_set.instances)

# print(f"Kalman filters initialized (frame {t})")
print(f"Kalman filters initialized (frame {t})")
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use logging instead of print statements for initialization messages.

Replace the print statement with the logging module to improve flexibility and control over log outputs.

Apply this diff:

+import logging
...
+logger = logging.getLogger(__name__)
...
-print(f"Kalman filters initialized (frame {t})")
+logger.info(f"Kalman filters initialized (frame {t})")

Committable suggestion was skipped due to low confidence.

Copy link

codecov bot commented Oct 8, 2024

Codecov Report

Attention: Patch coverage is 76.19048% with 5 lines in your changes missing coverage. Please review.

Project coverage is 76.13%. Comparing base (682823a) to head (fa5a82b).
Report is 3 commits behind head on develop.

Files with missing lines Patch % Lines
sleap/nn/tracking.py 77.77% 4 Missing ⚠️
sleap/nn/inference.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1986      +/-   ##
===========================================
+ Coverage    75.42%   76.13%   +0.70%     
===========================================
  Files          134      134              
  Lines        24749    24764      +15     
===========================================
+ Hits         18668    18854     +186     
+ Misses        6081     5910     -171     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@eberrigan eberrigan changed the title Elizabeth/set kalman filter args equal to max tracking args Allow max tracking args for Kalman filter Oct 8, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
sleap/io/format/coco.py (1)

183-185: LGTM! Consider adding documentation for this edge case.

The change correctly preserves spatial information for labeled but invisible keypoints, which is valuable for tracking algorithms. This enhancement aligns well with the PR's objective of improving Kalman filter tracking.

Consider adding a comment explaining this edge case:

     if flag == 0:
         # node not labeled for this instance
         if (x, y) != (0, 0):
+            # Some datasets mark points as unlabeled (flag=0) but still provide
+            # valid coordinates. We preserve this spatial information while
+            # marking the point as invisible for tracking purposes.
             points[node] = Point(x, y, False)
         continue
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2db959d and 7d09154.

📒 Files selected for processing (1)
  • sleap/io/format/coco.py (1 hunks)

Comment on lines 183 to 185
if (x, y) != (0, 0):
# If labeled but invisible, place the node at the coord
points[node] = Point(x, y, False)
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the search results, I can now provide a final response about the COCO format compliance:

Non-zero coordinates with flag=0 violates COCO keypoint format specification

The COCO keypoint format specifies that when visibility flag=0, the keypoint is not labeled and its coordinates should be (0,0). The current implementation that places points at non-zero coordinates when flag=0 deviates from this specification. This could cause compatibility issues with other COCO-compliant tools.

  • In sleap/io/format/coco.py, the condition if (x, y) != (0, 0) should be removed, and points with flag=0 should consistently have (0,0) coordinates.
🔗 Analysis chain

Verify compliance with COCO keypoint format specification.

While this change improves tracking capabilities, we should verify that handling flag=0 points with non-zero coordinates aligns with the COCO format specification.


Let me search for more specific information about COCO keypoint handling.


Let me try one more search focusing specifically on keypoint coordinate handling in COCO format.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for similar handling in other COCO implementations

# Look for similar handling in other COCO-related files
rg -A 5 "keypoints.*flag.*0" 

# Look for COCO format documentation references
rg -i "coco.*format.*specification"

Length of output: 174


Script:

#!/bin/bash
# Search for COCO keypoint handling patterns
rg -B 2 -A 2 "keypoints.*visibility" 

# Search for any comments or documentation about COCO format
rg -B 2 -A 2 "COCO.*format"

# Look for coordinate handling in COCO-related files
rg -B 2 -A 2 "x.*y.*visibility|visibility.*x.*y" sleap/io/format/

Length of output: 592


Script:

#!/bin/bash
# Search for keypoint coordinate handling in COCO format
rg -B 5 -A 5 "keypoints.*=|keypoints.*\[" sleap/io/format/coco.py

# Look for any documentation about visibility flags
rg -B 3 -A 3 "visibility.*flag|flag.*visibility" sleap/io/format/

# Search for coordinate handling patterns
ast-grep --pattern 'if ($x, $y) != (0, 0)'

Length of output: 1070

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
sleap/nn/inference.py (1)

1132-1136: Consider improving lambda function readability

The lambda function logic is correct but could be more readable. Consider reformatting to improve clarity.

-            lambda x: (
-                sleap.nn.data.utils.unrag_example(model(x), numpy=False)
-                if unrag_outputs
-                else model(x)
-            )
+            lambda x: sleap.nn.data.utils.unrag_example(model(x), numpy=False) 
+                     if unrag_outputs 
+                     else model(x)
sleap_tracking_debug.py (1)

8-13: Add error handling for file operations and data validation

The current implementation lacks error handling which could lead to unclear failures if the file is missing or corrupted.

Consider adding error handling:

-# Load predictions
-labels = sleap.load_file(PREDICTIONS_FILE)
+try:
+    labels = sleap.load_file(PREDICTIONS_FILE)
+    if not labels or not hasattr(labels, 'instances'):
+        raise ValueError("Invalid or empty predictions file")
+except Exception as e:
+    raise RuntimeError(f"Failed to load predictions from {PREDICTIONS_FILE}: {str(e)}")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4adf0a0 and 1587e48.

📒 Files selected for processing (2)
  • sleap/nn/inference.py (2 hunks)
  • sleap_tracking_debug.py (1 hunks)
🔇 Additional comments (2)
sleap/nn/inference.py (1)

5711-5714: LGTM!

Standard Python idiom for script execution. The implementation is correct and follows best practices.

sleap_tracking_debug.py (1)

15-25: Verify tracker configuration aligns with PR objectives

The configuration includes the new max_tracking and max_tracks parameters along with Kalman filter settings. This appears to address the issue mentioned in PR #1986.

Let's verify the tracker configuration matches the updated API:

Consider making the configuration more flexible:

+# Configuration could be moved to a separate config file or CLI arguments
+tracker_config = {
+    "tracker": "flow",
+    "track_window": 5,
+    "similarity": "instance",
+    "match": "hungarian",
+    "max_tracking": True,
+    "max_tracks": 2,
+    "kf_node_indices": [0, 1],
+    "kf_init_frame_count": 10,
+}
+tracker = sleap.nn.tracking.Tracker.make_tracker_by_name(**tracker_config)
✅ Verification successful

Tracker configuration is correctly implemented and documented

The verification confirms that:

  • The parameters max_tracking, max_tracks, and kf_init_frame_count are officially supported and documented in the CLI interface
  • The documentation shows these parameters are used exactly as implemented in the code:
    • max_tracking: Controls if tracker should cap max number of tracks (default: False)
    • max_tracks: Sets maximum number of tracks to be tracked (default: None)
    • kf_init_frame_count: Number of frames for Kalman filter initialization (default: 0)

The implementation aligns perfectly with the documented API and PR objectives.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the tracker parameters match the updated API
ast-grep --pattern 'make_tracker_by_name($$$)' | grep -A 5 'max_tracking\|max_tracks\|kf_init_frame_count'

# Verify if these parameters are documented
rg -A 5 'max_tracking|max_tracks|kf_init_frame_count' --type md

Length of output: 3554

Comment on lines 27 to 34
tracked_lfs = []
for lf in labels:
lf.instances = tracker.track(lf.instances, img=lf.image)
tracked_lfs.append(lf)
tracked_labels = sleap.Labels(tracked_lfs)
tracked_labels.save(
"/Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.tracked.slp"
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve robustness of tracking execution and saving

The tracking and saving implementation could benefit from several improvements:

  1. Output path has the same portability issues as input path
  2. No error handling for tracking operations
  3. No progress indication for potentially long operations

Consider these improvements:

+from tqdm import tqdm
+import os.path as osp
+
 tracked_lfs = []
-for lf in labels:
+# Add progress bar for visibility
+for lf in tqdm(labels, desc="Processing frames"):
+    try:
         lf.instances = tracker.track(lf.instances, img=lf.image)
         tracked_lfs.append(lf)
+    except Exception as e:
+        print(f"Warning: Failed to process frame {lf.frame_idx}: {str(e)}")
+
 tracked_labels = sleap.Labels(tracked_lfs)
+
+# Use relative output path
+output_path = osp.join(osp.dirname(__file__), "output", "clip.2node.tracked.slp")
+os.makedirs(osp.dirname(output_path), exist_ok=True)
+
+try:
-tracked_labels.save(
-    "/Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.tracked.slp"
-)
+    tracked_labels.save(output_path)
+except Exception as e:
+    raise RuntimeError(f"Failed to save tracked labels to {output_path}: {str(e)}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tracked_lfs = []
for lf in labels:
lf.instances = tracker.track(lf.instances, img=lf.image)
tracked_lfs.append(lf)
tracked_labels = sleap.Labels(tracked_lfs)
tracked_labels.save(
"/Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.tracked.slp"
)
from tqdm import tqdm
import os.path as osp
tracked_lfs = []
# Add progress bar for visibility
for lf in tqdm(labels, desc="Processing frames"):
try:
lf.instances = tracker.track(lf.instances, img=lf.image)
tracked_lfs.append(lf)
except Exception as e:
print(f"Warning: Failed to process frame {lf.frame_idx}: {str(e)}")
tracked_labels = sleap.Labels(tracked_lfs)
# Use relative output path
output_path = osp.join(osp.dirname(__file__), "output", "clip.2node.tracked.slp")
os.makedirs(osp.dirname(output_path), exist_ok=True)
try:
tracked_labels.save(output_path)
except Exception as e:
raise RuntimeError(f"Failed to save tracked labels to {output_path}: {str(e)}")

Comment on lines 3 to 5
PREDICTIONS_FILE = (
"/Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.slp"
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace hardcoded absolute path with a more portable solution

The current hardcoded path contains system-specific information and won't work across different environments.

Consider using one of these approaches:

-PREDICTIONS_FILE = (
-    "/Users/elizabethberrigan/repos/sleap/tests/data/tracks/clip.2node.slp"
-)
+# Option 1: Use relative path from script location
+import os
+PREDICTIONS_FILE = os.path.join(os.path.dirname(__file__), "tests/data/tracks/clip.2node.slp")
+
+# Option 2: Use environment variable
+PREDICTIONS_FILE = os.getenv("SLEAP_TEST_DATA_PATH", "tests/data/tracks/clip.2node.slp")

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/nn/test_tracking_integration.py (1)

Line range hint 1-236: Add test coverage for Kalman filter functionality.

Given that this PR addresses Kalman filter issues with target_instance_count, we should add test cases to verify:

  1. Kalman filter initialization with kf_init_frame_count
  2. Proper handling of target_instance_count/max_tracks
  3. Integration with the GUI parameters

Consider adding test cases similar to test_simple_tracker and test_simplemax_tracker but specifically for Kalman filter scenarios. This will help prevent regression issues.

Would you like me to help generate these test cases?

🧰 Tools
🪛 Ruff (0.8.2)

149-149: Local variable kalman_params is assigned to but never used

Remove assignment to unused variable kalman_params

(F841)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1587e48 and 92ab0fd.

📒 Files selected for processing (1)
  • tests/nn/test_tracking_integration.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/nn/test_tracking_integration.py

149-149: Local variable kalman_params is assigned to but never used

Remove assignment to unused variable kalman_params

(F841)

Comment on lines 149 to 152
kalman_params = dict(
kf_node_indices=[0, 1],
kf_init_frame_count=10,
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unused variable or implement Kalman filter tests.

The kalman_params dictionary is defined but never used in the test code. Either:

  1. Remove the unused variable, or
  2. Implement test cases to verify the Kalman filter functionality with these parameters.

If you'd like, I can help:

  1. Remove the unused code:
-    kalman_params = dict(
-        kf_node_indices=[0, 1],
-        kf_init_frame_count=10,
-    )
  1. Generate test cases for the Kalman filter functionality that utilize these parameters.
🧰 Tools
🪛 Ruff (0.8.2)

149-149: Local variable kalman_params is assigned to but never used

Remove assignment to unused variable kalman_params

(F841)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
sleap/nn/tracking.py (3)

831-839: Use logging and improve parameter handling.

Two suggestions:

  1. Replace print statement with proper logging
  2. Consider adding a deprecation warning when target_instance_count is used

Apply this diff:

+import logging
+import warnings
+
+logger = logging.getLogger(__name__)
+
 elif (
     self.target_instance_count or self.max_tracks
 ) and self.post_connect_single_breaks:
     if not self.target_instance_count:
         # If target_instance_count is not set, use max_tracks instead
         # target_instance_count not available in the GUI
         self.target_instance_count = self.max_tracks
+    else:
+        warnings.warn(
+            "target_instance_count is deprecated and will be removed in a future version. "
+            "Use max_tracks instead.",
+            DeprecationWarning,
+            stacklevel=2
+        )
     connect_single_track_breaks(frames, self.target_instance_count)
-    print("Connecting single track breaks.")
+    logger.info("Connecting single track breaks.")

Line range hint 959-995: LGTM! Consider improving error messages.

The validation logic for Kalman filter requirements is comprehensive and handles all edge cases correctly. The code properly falls back to max_tracks when target_instance_count is not set.

Consider making the error messages more user-friendly by providing guidance on how to fix the issues:

-    raise ValueError(
-        "Kalman filter requires node indices for instance tracking."
-    )
+    raise ValueError(
+        "Kalman filter requires node indices for instance tracking. "
+        "Please specify node indices using --tracking.kf_node_indices."
+    )

-    raise ValueError(
-        "Kalman filter requires simple tracker for initial tracking."
-    )
+    raise ValueError(
+        "Kalman filter requires simple tracker for initial tracking. "
+        "Please use --tracking.tracker simple or simplemaxtracks."
+    )

-    raise ValueError(
-        "Kalman filter does not support normalized_instance_similarity."
-    )
+    raise ValueError(
+        "Kalman filter does not support normalized_instance_similarity. "
+        "Please use a different similarity metric like instance, centroid, or iou."
+    )

-    raise ValueError(
-        "Kalman filter requires max tracks or target instance count."
-    )
+    raise ValueError(
+        "Kalman filter requires max tracks or target instance count. "
+        "Please specify --tracking.max_tracks or --tracking.target_instance_count."
+    )

1409-1411: Use logging instead of print statement.

Replace print statement with proper logging for better control over log levels.

Apply this diff:

+import logging
+
+logger = logging.getLogger(__name__)
+
-print(
-    f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters."
-)
+logger.info(
+    f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters."
+)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15deb3f and 2342a9d.

📒 Files selected for processing (3)
  • docs/guides/cli.md (1 hunks)
  • sleap/nn/tracking.py (12 hunks)
  • tests/nn/test_tracking_integration.py (1 hunks)
🔇 Additional comments (3)
sleap/nn/tracking.py (1)

1465-1465: Use logging instead of print statement.

Replace print statement with proper logging for better control over log levels.

Apply this diff:

-print(f"Kalman filters initialized (frame {t})")
+logger.info(f"Kalman filters initialized (frame {t})")
tests/nn/test_tracking_integration.py (1)

22-181: LGTM! Comprehensive test coverage.

The test function thoroughly covers:

  1. Error cases for incompatible trackers (flow, flowmaxtracks)
  2. Error cases for normalized_instance similarity
  3. Error cases for missing kf_node_indices
  4. Error cases for missing max_tracks and target_instance_count
  5. Success cases with various combinations of parameters

The assertions verify that the number of tracks matches the expected count.

docs/guides/cli.md (1)

233-233: LGTM! Clear documentation of Kalman filter requirements.

The documentation accurately describes the requirements for using Kalman filters, matching the implementation in the code:

  1. Need for kf_node_indices
  2. Need for max_tracking and max_tracks or target_instance_count
  3. Requirement for simple or simplemaxtracks tracker
  4. Incompatibility with normalized_instance similarity

Comment on lines 290 to 291
print(f"Image type before converting: {type(ref_img)}")

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove debug print statements and use logging.

Debug print statements should be replaced with proper logging for better control over log levels and output.

Apply this diff:

+import logging
+
+logger = logging.getLogger(__name__)
+
-print(f"Image type before converting: {type(ref_img)}")
+logger.debug(f"Image type before converting: {type(ref_img)}")

-print(f"Image type after converting: {type(ref_img)}")
+logger.debug(f"Image type after converting: {type(ref_img)}")

Also applies to: 296-297

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
docs/guides/cli.md (1)

288-292: Consider enhancing the Kalman tracker example.

While the example demonstrates the correct usage of Kalman tracker with required parameters, consider these improvements:

  1. Add a comment explaining why flow tracker is preferred over Kalman
  2. Include an example with pre_cull_to_target option since it's mentioned in the PR objectives

Apply this diff to enhance the example:

 **9. Use Kalman tracker (not recommended since flow is preferred):**
 
+# Note: Flow tracker is preferred as it provides better tracking accuracy and stability
+
 ```none
 sleap-track -m "models/my_model" --tracking.similarity instance --tracking.tracker simplemaxtracks --tracking.max_tracking 1 --tracking.max_tracks 4 --tracking.kf_init_frame_count 10 --tracking.kf_node_indices 0,1 -o "output_predictions.slp" "input_video.mp4"

+# Alternative: Using Kalman tracker with pre_cull_to_target:
+none +sleap-track -m "models/my_model" --tracking.similarity instance --tracking.tracker simplemaxtracks --tracking.max_tracking 1 --tracking.target_instance_count 4 --tracking.pre_cull_to_target 1 --tracking.kf_init_frame_count 10 --tracking.kf_node_indices 0,1 -o "output_predictions.slp" "input_video.mp4" +


</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 2342a9d0e10687ca143c332a7eb9ddce91ddccae and f9a047632d52051203219a936bb989f3c323bf4c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `docs/guides/cli.md` (2 hunks)

</details>

<details>
<summary>🔇 Additional comments (1)</summary>

<details>
<summary>docs/guides/cli.md (1)</summary>

`233-233`: **Documentation accurately describes Kalman filter requirements.**

The documentation clearly lists all the required parameters for using Kalman filters, which directly addresses the issue mentioned in PR #1986 regarding the target_instance_count requirement.

</details>

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
sleap/nn/tracking.py (2)

1405-1407: Use logging instead of print statements.

Replace print statement with proper logging for better control over log output.

Apply this diff:

-        print(
-            f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters."
-        )
+        logger = logging.getLogger(__name__)
+        logger.info(
+            f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters."
+        )

1461-1461: Use logging for Kalman filter initialization message.

Replace print statement with proper logging.

Apply this diff:

-                print(f"Kalman filters initialized (frame {t})")
+                logger = logging.getLogger(__name__)
+                logger.info(f"Kalman filters initialized (frame {t})")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9a0476 and 32aa5ee.

📒 Files selected for processing (2)
  • sleap/nn/tracking.py (9 hunks)
  • tests/nn/test_tracking_integration.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/nn/test_tracking_integration.py
🔇 Additional comments (2)
sleap/nn/tracking.py (2)

Line range hint 955-991: LGTM! Well-structured validation for Kalman filter requirements.

The validation logic is comprehensive and includes clear error messages for:

  • Required node indices
  • Incompatible tracker types
  • Unsupported similarity functions
  • Missing max_tracks or target_instance_count

827-835: 🛠️ Refactor suggestion

Fix indentation and improve logging.

The indentation is inconsistent, and the print statement should be replaced with proper logging.

Apply this diff to fix indentation and use logging:

-        elif (
-            self.target_instance_count or self.max_tracks
-        ) and self.post_connect_single_breaks:
-            if not self.target_instance_count:
-                # If target_instance_count is not set, use max_tracks instead
-                # target_instance_count not available in the GUI
-                self.target_instance_count = self.max_tracks
-            connect_single_track_breaks(frames, self.target_instance_count)
-            print("Connecting single track breaks.")
+        elif (self.target_instance_count or self.max_tracks) and self.post_connect_single_breaks:
+            if not self.target_instance_count:
+                # If target_instance_count is not set, use max_tracks instead
+                # target_instance_count not available in the GUI
+                self.target_instance_count = self.max_tracks
+            connect_single_track_breaks(frames, self.target_instance_count)
+            logger = logging.getLogger(__name__)
+            logger.info("Connecting single track breaks.")

Likely invalid or redundant comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (14)
sleap/info/summary.py (1)

250-280: LGTM! Well-implemented tracking score series calculation.

The implementation:

  • Follows consistent pattern with other series methods
  • Properly handles missing tracking scores and NaN values
  • Provides appropriate reduction methods for tracking scores

However, consider adding input validation for the reduction parameter.

Consider adding validation:

 def get_tracking_score_series(
     self, video: Video, reduction: str = "min"
 ) -> Dict[int, float]:
+    if reduction not in ["min", "mean"]:
+        raise ValueError(f"Invalid reduction method: {reduction}. Must be one of: min, mean")
     reduce_fn = {
         "min": np.nanmin,
         "mean": np.nanmean,
     }[reduction]
sleap/gui/dataviews.py (1)

20-21: Consider grouping standard library imports.
The imports from pathlib and typing are standard library modules. It's often best practice to group or order standard library, third-party, and local imports consistently. However, only do so if it aligns with existing conventions in the repository.

sleap/gui/widgets/video.py (4)

1195-1211: Pinch gesture logic is straightforward and well-scoped.
Scale factor usage is intuitive and updates the viewer properly. However, ensure that we do not exceed any maximum zoom constraints.

 if gesture:
     self.handlePinchGesture(gesture)

 def handlePinchGesture(self, gesture: QPinchGesture):
     if gesture.state() == Qt.GestureState.GestureUpdated:
         factor = gesture.scaleFactor()
-        self.zoomFactor = max(factor * self.zoomFactor, 1)
+        # Optionally add an upper bound for self.zoomFactor, e.g., 10
+        new_zoom = factor * self.zoomFactor
+        self.zoomFactor = max(min(new_zoom, 10), 1)
         self.updateViewer()

1631-1634: Hover events are added but no custom logic is present.
Currently, hoverEnterEvent and hoverLeaveEvent do not contain custom behavior. Verify if we need tooltips or style changes for user feedback.


2018-2023: Hover-based opacity adjustment is elegant.
This logic adjusts overall bounding box opacity based on selection and hover states—straightforward and user-friendly. Consider adding a highlight color or dash style to differentiate from selection.


2317-2317: Ensure box transform updates remain minimal.
Currently, we’re scaling node positions upon mouse release. That is correct, but be sure to test large bounding box changes so that node positions remain valid.

Would you like me to provide an automated script to test large bounding box transformations across frames?

sleap/gui/dialogs/frame_range.py (1)

39-42: Add more comprehensive test cases

The main block could include more test cases to verify edge cases.

Consider adding tests for:

  • Edge cases (min = max)
  • Invalid ranges (min > max)
  • Boundary conditions (max_frame_idx = 1)
docs/guides/index.md (1)

33-35: Consider rephrasing for variety

The documentation addition is good, but consider rephrasing to avoid repetitive "when you want to" pattern.

Suggested revision:

-{ref}`bonsai` when you want to analyze the trained SLEAP model to visualize the poses, centroids and identities for further visual analysis.
+{ref}`bonsai` provides tools for analyzing trained SLEAP models, enabling visualization of poses, centroids, and identities for detailed visual analysis.
🧰 Tools
🪛 LanguageTool

[style] ~35-~35: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...EAP with Bonsai {ref}bonsai when you want to analyze the trained SLEAP model to visu...

(REP_WANT_TO_VB)

sleap/prefs.py (1)

48-55: Enhance error handling and logging

The current error handling silently ignores file not found errors and might mask other potential issues.

Consider this enhancement:

     try:
         self._prefs = util.get_config_yaml(self._filename)
     except FileNotFoundError:
-        pass
+        logger.debug(f"Preferences file {self._filename} not found, using defaults")
+    except Exception as e:
+        logger.warning(f"Error loading preferences: {e}, using defaults")
 
     self._prefs = self._prefs or {}
 
     for k, v in self._defaults.items():
         if k not in self._prefs:
+            logger.debug(f"Setting default preference for {k}: {v}")
             self._prefs[k] = v
docs/guides/bonsai.md (2)

11-11: Fix heading level increment.

The heading level jumps from h1 to h3. Heading levels should only increment by one level at a time.

-### Exporting a SLEAP trained model
+## Exporting a SLEAP trained model
🧰 Tools
🪛 Markdownlint (0.37.0)

11-11: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)


73-73: Add comma in compound sentence.

Add a comma before 'and' as it connects two independent clauses.

-Now you can click the green start button to run the workflow and you can add more modules to analyze and visualize the results in Bonsai.
+Now you can click the green start button to run the workflow, and you can add more modules to analyze and visualize the results in Bonsai.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~73-~73: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...e green start button to run the workflow and you can add more modules to analyze and...

(COMMA_COMPOUND_SENTENCE)

tests/gui/learning/test_dialog.py (1)

436-441: Remove unused variable.

The app variable is assigned but never used in the test function.

-    app = MainWindow(no_usage_data=True)
     ld = LearningDialog(
         mode="training",
         labels_filename=Path(min_labels_slp_path),
         labels=min_labels_slp,
     )
🧰 Tools
🪛 Ruff (0.8.2)

436-436: Local variable app is assigned to but never used

Remove assignment to unused variable app

(F841)

tests/gui/test_commands.py (1)

855-874: LGTM! Consider enhancing test coverage.

The test correctly verifies the basic functionality of DeleteFrameLimitPredictions. However, consider adding more test cases to cover:

  • Edge cases (empty frame range, invalid frame indices)
  • Different frame range combinations
  • Verification of which instances are actually deleted
sleap/gui/learning/dialog.py (1)

640-653: LGTM! Consider these improvements.

The _validate_id_model implementation correctly checks for tracks, but has a few issues:

  1. Replace type comparison with isinstance:
-if type(inst) == sleap.Instance and inst.track is not None:
+if isinstance(inst, sleap.Instance) and inst.track is not None:
  1. Remove unused variable:
-        message = "Cannot run ID model training without tracks."
         return False
🧰 Tools
🪛 Ruff (0.8.2)

643-643: Local variable message is assigned to but never used

Remove assignment to unused variable message

(F841)


648-648: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32aa5ee and 6dc7a12.

⛔ Files ignored due to path filters (6)
  • docs/_static/bonsai-connection.jpg is excluded by !**/*.jpg
  • docs/_static/bonsai-filecapture.jpg is excluded by !**/*.jpg
  • docs/_static/bonsai-predictcentroids.jpg is excluded by !**/*.jpg
  • docs/_static/bonsai-predictposeidentities.jpg is excluded by !**/*.jpg
  • docs/_static/bonsai-predictposes.jpg is excluded by !**/*.jpg
  • docs/_static/bonsai-workflow.jpg is excluded by !**/*.jpg
📒 Files selected for processing (19)
  • .github/workflows/website.yml (1 hunks)
  • docs/guides/bonsai.md (1 hunks)
  • docs/guides/index.md (2 hunks)
  • docs/installation.md (4 hunks)
  • sleap/config/frame_range_form.yaml (1 hunks)
  • sleap/gui/app.py (6 hunks)
  • sleap/gui/commands.py (3 hunks)
  • sleap/gui/dataviews.py (2 hunks)
  • sleap/gui/dialogs/frame_range.py (1 hunks)
  • sleap/gui/learning/dialog.py (2 hunks)
  • sleap/gui/widgets/video.py (9 hunks)
  • sleap/info/summary.py (7 hunks)
  • sleap/instance.py (10 hunks)
  • sleap/nn/inference.py (5 hunks)
  • sleap/prefs.py (2 hunks)
  • tests/fixtures/datasets.py (1 hunks)
  • tests/gui/learning/test_dialog.py (2 hunks)
  • tests/gui/test_commands.py (2 hunks)
  • tests/info/test_summary.py (1 hunks)
👮 Files not reviewed due to content moderation or server errors (3)
  • sleap/gui/app.py
  • sleap/gui/commands.py
  • sleap/nn/inference.py
🧰 Additional context used
🪛 Ruff (0.8.2)
sleap/gui/learning/dialog.py

643-643: Local variable message is assigned to but never used

Remove assignment to unused variable message

(F841)


648-648: Use is and is not for type comparisons, or isinstance() for isinstance checks

(E721)

sleap/gui/dialogs/frame_range.py

27-27: Local variable min_frame_idx_field is assigned to but never used

Remove assignment to unused variable min_frame_idx_field

(F841)


34-34: Local variable max_frame_idx_field is assigned to but never used

Remove assignment to unused variable max_frame_idx_field

(F841)

tests/gui/learning/test_dialog.py

436-436: Local variable app is assigned to but never used

Remove assignment to unused variable app

(F841)

sleap/gui/dataviews.py

395-395: Undefined name Video

(F821)

sleap/nn/inference.py

4459-4459: Loop control variable image not used within loop body

Rename unused image to _image

(B007)

🪛 yamllint (1.35.1)
sleap/config/frame_range_form.yaml

[error] 13-13: no new line character at the end of file

(new-line-at-end-of-file)

🪛 LanguageTool
docs/guides/bonsai.md

[uncategorized] ~73-~73: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...e green start button to run the workflow and you can add more modules to analyze and...

(COMMA_COMPOUND_SENTENCE)

docs/guides/index.md

[style] ~35-~35: You have already used this phrasing in nearby sentences. Consider replacing it to add variety to your writing.
Context: ...EAP with Bonsai {ref}bonsai when you want to analyze the trained SLEAP model to visu...

(REP_WANT_TO_VB)

🪛 Markdownlint (0.37.0)
docs/guides/bonsai.md

11-11: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)

🔇 Additional comments (18)
sleap/instance.py (1)

Line range hint 1052-1076: LGTM! Consistent implementation of tracking score parameter.

The tracking score parameter has been properly added across all factory methods with consistent default values and parameter passing. This change aligns with the PR objectives for fixing the Kalman filter tracking functionality.

Also applies to: 1087-1087, 1102-1102, 1123-1123, 1134-1134, 1149-1149, 1160-1160, 1171-1171, 1186-1186, 1197-1197

tests/info/test_summary.py (1)

44-55: LGTM! Comprehensive test coverage for tracking scores.

The test thoroughly validates the new get_tracking_score_series functionality:

  • Tests both reduction methods ("min" and "mean")
  • Verifies expected array length
  • Validates specific score values at different frames
sleap/info/summary.py (2)

24-24: LGTM! Documentation formatting improvements.

Consistent use of backticks for class references improves readability.

Also applies to: 44-44, 70-70, 96-96, 124-124, 229-229


250-280: Verify integration with Kalman filter tracking.

Let's verify that this implementation properly supports the Kalman filter tracking functionality mentioned in the PR objectives.

✅ Verification successful

Implementation properly supports tracking functionality

The verification shows that:

  • The tracking score implementation is properly integrated into the tracking system as evidenced by the tracking score assignment in sleap/nn/tracking.py
  • The functionality is well-tested as shown in tests/info/test_summary.py which verifies the tracking score series calculation
  • The get_tracking_score_series method correctly aggregates tracking scores across frames, supporting the tracking system's needs
  • The implementation allows for both minimum and mean score calculations, providing flexibility in tracking score analysis
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Kalman filter integration with tracking scores

# Check for Kalman filter usage of tracking scores
rg -l "tracking_score.*Kalman" --type py

# Check for related configuration or parameter handling
rg -l "target_instance_count.*Kalman" --type py

Length of output: 92


Script:

#!/bin/bash
# Let's search for Kalman filter implementation and tracking score usage separately

# Find files containing Kalman filter implementation
rg -l "class.*Kalman" --type py

# Find files containing tracking score usage
rg -l "tracking_score" --type py

# Search for tracking-related configuration
rg -l "tracking.*config" --type py

Length of output: 443


Script:

#!/bin/bash
# Let's examine the Kalman filter implementation and its relationship with tracking scores

# Check Kalman filter implementation for tracking score usage
rg -A 5 -B 5 "tracking_score" sleap/nn/tracker/kalman.py

# Check tracking.py for the connection between Kalman and tracking scores
rg -A 5 -B 5 "tracking_score" sleap/nn/tracking.py

# Look at the instance class to understand tracking score implementation
ast-grep --pattern 'class LabeledInstance {
  $$$
  tracking_score
  $$$
}'

Length of output: 504


Script:

#!/bin/bash
# Let's examine the tracking implementation more closely

# Look for tracking score assignment in tracking.py
ast-grep --pattern 'def track($$$) {
  $$$
  tracking_score
  $$$
}'

# Check test coverage for tracking scores
rg -A 5 -B 5 "test.*tracking_score" tests/info/test_summary.py

# Look for the relationship between tracking and instances
rg -A 5 "def track" sleap/nn/tracking.py

Length of output: 1008

sleap/gui/dataviews.py (3)

23-24: Imports look fine.
No issues found with importing numpy or Qt modules.


386-394: Property list aligns well with the new “name” and “filepath” fields.
The updated tuple for properties is clear and coherent.


27-28: Ensure circular dependencies are avoided.
These imports from the sleap.gui package appear top-level. Verify that no cross-module references cause circular import issues, especially since these appear in a core GUI model.

✅ Verification successful

Based on the analysis of the import patterns and content of the files, I can now provide the final response:

No circular dependency issues detected in these imports.
The imports are safe as the dependency graph is unidirectional:

  • state.py is a foundational module with no GUI-related imports
  • dataviews.py imports from state.py and commands.py
  • No module that imports dataviews.py is imported by dataviews.py
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Checking for circular imports that involve sleap/gui/dataviews.py and sleap/gui/commands.py or sleap/gui/state.py
# We'll search for import chains that might produce cycles.
ast-grep --pattern $'import $_ from sleap.gui.commands' 
ast-grep --pattern $'import $_ from sleap.gui.state'

Length of output: 110


Script:

#!/bin/bash
# Let's try a different approach using ripgrep to find import patterns
echo "=== Checking imports in sleap/gui/dataviews.py ==="
rg "^from.*import|^import" sleap/gui/dataviews.py

echo -e "\n=== Checking where dataviews.py is imported ==="
rg "from.*dataviews.*import|import.*dataviews" --type py

echo -e "\n=== Checking imports in sleap/gui/state.py ==="
rg "^from.*import|^import" sleap/gui/state.py

echo -e "\n=== Checking where state.py is imported ==="
rg "from.*state.*import|import.*state" --type py

Length of output: 1698


Script:

#!/bin/bash
# Let's check the content of commands.py to see if it imports dataviews
echo "=== Checking imports in sleap/gui/commands.py ==="
rg "^from.*import|^import" sleap/gui/commands.py

# Let's also check the content of state.py to see what it contains
echo -e "\n=== Content of sleap/gui/state.py ==="
cat sleap/gui/state.py

Length of output: 8163

sleap/gui/widgets/video.py (4)

65-65: Good addition for pinch gesture support.
This import will facilitate zoom features, which enhances user experience.


1834-1834: No immediate issues with _is_hovering initialization.
This boolean is tracked to detect hover state changes.


1856-1856: Accepting hover events can be beneficial.
The call to setAcceptHoverEvents(True) ensures that hover events are dispatched.


2117-2126: Hover event overrides in QtInstance.
The code is consistent with the approach in Qt. The user will get real-time highlight feedback. Looks good.

.github/workflows/website.yml (1)

11-11: Branch trigger updated to 'liezl/add-channels-for-pip-conda-env'.
This is a simple and valid workflow config change. Ensure the relevant branch name is correct and that no other references remain to the old branch.

sleap/prefs.py (1)

31-32: LGTM: New default preferences

The new default preferences for node marker and label sizes are well-structured and provide good granularity for UI customization.

docs/guides/bonsai.md (1)

1-75: Well-structured and comprehensive documentation!

The documentation provides clear instructions for using Bonsai with SLEAP models, including installation steps, module usage, and workflow configuration. The inclusion of visual aids enhances understanding.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~73-~73: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...e green start button to run the workflow and you can add more modules to analyze and...

(COMMA_COMPOUND_SENTENCE)

🪛 Markdownlint (0.37.0)

11-11: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)

tests/fixtures/datasets.py (1)

100-112: Well-implemented test fixture!

The fixture is properly documented with generation instructions and follows the established pattern of other fixtures in the file. The implementation is clean and consistent.

tests/gui/learning/test_dialog.py (1)

435-451: Well-structured test implementation!

The test thoroughly validates the ID model under different conditions:

  1. Initial state without tracks
  2. With track but unassigned
  3. With track assigned to instances

The test progression and assertions are clear and effective.

🧰 Tools
🪛 Ruff (0.8.2)

436-436: Local variable app is assigned to but never used

Remove assignment to unused variable app

(F841)

docs/installation.md (1)

30-30: LGTM! Good documentation enhancement.

Adding a note about alternative terminal apps like Cmder and Windows Terminal improves the user experience for Windows users.

sleap/gui/learning/dialog.py (1)

672-680: LGTM! Good integration with pipeline validation.

The changes correctly integrate ID model validation into the pipeline validation process.

Comment on lines 395 to 404
def item_to_data(self, obj, item: "Video"):
data = {}
for property in self.properties:
if property == "name":
data[property] = Path(item.filename).name
elif property == "filepath":
data[property] = str(Path(item.filename).parent)
else:
data[property] = getattr(item, property)
return data
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address missing type annotation reference "Video".
You have “item: ‘Video’” in the signature, but “Video” is neither imported at the top of this file nor aliased locally. This causes a name error in some static checks.

Apply the following diff to fix the name error:

+from sleap.io.video import Video

 def item_to_data(self, obj, item: "Video"):
     data = {}
     ...
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def item_to_data(self, obj, item: "Video"):
data = {}
for property in self.properties:
if property == "name":
data[property] = Path(item.filename).name
elif property == "filepath":
data[property] = str(Path(item.filename).parent)
else:
data[property] = getattr(item, property)
return data
from sleap.io.video import Video
def item_to_data(self, obj, item: "Video"):
data = {}
for property in self.properties:
if property == "name":
data[property] = Path(item.filename).name
elif property == "filepath":
data[property] = str(Path(item.filename).parent)
else:
data[property] = getattr(item, property)
return data
🧰 Tools
🪛 Ruff (0.8.2)

395-395: Undefined name Video

(F821)

Comment on lines 827 to 828
self.grabGesture(Qt.GestureType.PinchGesture)

Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Based on the analysis, I can now provide the final response:

Gesture handling needs test coverage
The pinch gesture implementation in video.py lacks test coverage. While the codebase has tests for the video player widget including mouse wheel events, there are no tests specifically covering gesture interactions. Key areas needing test coverage:

  • handleGestureEvent method
  • handlePinchGesture method
  • Zoom factor changes through pinch gestures
  • Gesture state handling
🔗 Analysis chain

Ensure gesture grabbing is properly tested.
Grabbing the pinch gesture can interfere with other gestures on certain platforms or older Qt versions. Validate in multiple environments if possible.


Let me search for gesture-related code and tests to better understand the implementation and testing coverage.


Let me check the test files to see if they cover gesture handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Searching for usage references to "grabGesture" within the codebase to ensure there's no conflict
rg "grabGesture"

Length of output: 97


Script:

#!/bin/bash
# Search for gesture-related code and tests
echo "=== Searching for gesture-related code ==="
rg -i "gesture" -A 3

echo -e "\n=== Searching for test files related to video widget ==="
fd "test.*video.*\.py$"

Length of output: 1750


Script:

#!/bin/bash
# Check test coverage for gesture handling
echo "=== Checking video player tests ==="
cat tests/gui/test_video_player.py

echo -e "\n=== Looking for any Qt gesture tests in the entire codebase ==="
rg -i "test.*gesture|gesture.*test" -A 3

Length of output: 5712

Comment on lines 1 to 13
main:

- name: min_frame_idx
label: Minimum frame index
type: int
range: 1,1000000
default: 1

- name: max_frame_idx
label: Maximum frame index
type: int
range: 1,1000000
default: 1000
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add a blank line at file end to satisfy lint.
YAML rules often require a trailing newline.

 range: 1,1000000
 default: 1000
+ 
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
main:
- name: min_frame_idx
label: Minimum frame index
type: int
range: 1,1000000
default: 1
- name: max_frame_idx
label: Maximum frame index
type: int
range: 1,1000000
default: 1000
main:
- name: min_frame_idx
label: Minimum frame index
type: int
range: 1,1000000
default: 1
- name: max_frame_idx
label: Maximum frame index
type: int
range: 1,1000000
default: 1000
🧰 Tools
🪛 yamllint (1.35.1)

[error] 13-13: no new line character at the end of file

(new-line-at-end-of-file)

Comment on lines 26 to 31
def _update_max_frame_range(self, value):
min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
max_frame_idx_field = self.form_widget.fields["max_frame_idx"]

max_frame_idx_field.setRange(value, max_frame_idx_field.maximum())

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused variable and improve range validation

The min_frame_idx_field variable is declared but never used. Additionally, the range validation could be more robust.

Apply this diff:

 def _update_max_frame_range(self, value):
-    min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
     max_frame_idx_field = self.form_widget.fields["max_frame_idx"]
 
-    max_frame_idx_field.setRange(value, max_frame_idx_field.maximum())
+    current_max = max_frame_idx_field.value()
+    max_frame_idx_field.setRange(value, max_frame_idx_field.maximum())
+    if current_max < value:
+        max_frame_idx_field.setValue(value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _update_max_frame_range(self, value):
min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
max_frame_idx_field = self.form_widget.fields["max_frame_idx"]
max_frame_idx_field.setRange(value, max_frame_idx_field.maximum())
def _update_max_frame_range(self, value):
max_frame_idx_field = self.form_widget.fields["max_frame_idx"]
current_max = max_frame_idx_field.value()
max_frame_idx_field.setRange(value, max_frame_idx_field.maximum())
if current_max < value:
max_frame_idx_field.setValue(value)
🧰 Tools
🪛 Ruff (0.8.2)

27-27: Local variable min_frame_idx_field is assigned to but never used

Remove assignment to unused variable min_frame_idx_field

(F841)

Comment on lines 32 to 37
def _update_min_frame_range(self, value):
min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
max_frame_idx_field = self.form_widget.fields["max_frame_idx"]

min_frame_idx_field.setRange(min_frame_idx_field.minimum(), value)

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused variable and improve range validation

Similar to above, max_frame_idx_field is unused and range validation could be improved.

Apply this diff:

 def _update_min_frame_range(self, value):
     min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
-    max_frame_idx_field = self.form_widget.fields["max_frame_idx"]
 
-    min_frame_idx_field.setRange(min_frame_idx_field.minimum(), value)
+    current_min = min_frame_idx_field.value()
+    min_frame_idx_field.setRange(min_frame_idx_field.minimum(), value)
+    if current_min > value:
+        min_frame_idx_field.setValue(value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _update_min_frame_range(self, value):
min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
max_frame_idx_field = self.form_widget.fields["max_frame_idx"]
min_frame_idx_field.setRange(min_frame_idx_field.minimum(), value)
def _update_min_frame_range(self, value):
min_frame_idx_field = self.form_widget.fields["min_frame_idx"]
current_min = min_frame_idx_field.value()
min_frame_idx_field.setRange(min_frame_idx_field.minimum(), value)
if current_min > value:
min_frame_idx_field.setValue(value)
🧰 Tools
🪛 Ruff (0.8.2)

34-34: Local variable max_frame_idx_field is assigned to but never used

Remove assignment to unused variable max_frame_idx_field

(F841)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
.github/ISSUE_TEMPLATE/bug_report.md (1)

32-36: Adjust list indentation for better markdown compatibility

The installation method checklist is well-structured with proper links, but the indentation should be adjusted to follow markdown best practices.

Apply this change to use 2-space indentation for the nested list:

-    -   [ ] [Conda from package](https://sleap.ai/installation.html#conda-package)
-    -   [ ] [Conda from source](https://sleap.ai/installation.html#conda-from-source)
-    -   [ ] [pip package](https://sleap.ai/installation.html#pip-package)
-    -   [ ] [Apple Silicon Macs](https://sleap.ai/installation.html#apple-silicon-macs)
+  - [ ] [Conda from package](https://sleap.ai/installation.html#conda-package)
+  - [ ] [Conda from source](https://sleap.ai/installation.html#conda-from-source)
+  - [ ] [pip package](https://sleap.ai/installation.html#pip-package)
+  - [ ] [Apple Silicon Macs](https://sleap.ai/installation.html#apple-silicon-macs)
🧰 Tools
🪛 Markdownlint (0.37.0)

33-33: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


34-34: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


35-35: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


36-36: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)

docs/conf.py (1)

31-31: Consider retaining dynamic version reading

While the version numbers are correctly synchronized with version.py, consider keeping the commented-out dynamic version reading code. This would:

  1. Ensure versions stay in sync automatically
  2. Reduce the risk of version mismatches
  3. Simplify future version updates

Uncomment and update the version reading code:

-version = "1.4.1"
+# Get the sleap version
+with open("../sleap/version.py") as f:
+    version_file = f.read()
+    version = re.search("\d.+(?=['\"])", version_file).group(0)

Also applies to: 39-39

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc7a12 and 2e260ae.

📒 Files selected for processing (7)
  • .github/ISSUE_TEMPLATE/bug_report.md (1 hunks)
  • .github/workflows/build_conda_ci.yml (1 hunks)
  • .github/workflows/build_pypi_ci.yml (1 hunks)
  • .github/workflows/website.yml (1 hunks)
  • docs/conf.py (2 hunks)
  • docs/installation.md (7 hunks)
  • sleap/version.py (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • .github/workflows/build_pypi_ci.yml
  • .github/workflows/build_conda_ci.yml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/website.yml
  • docs/installation.md
🧰 Additional context used
🪛 Markdownlint (0.37.0)
.github/ISSUE_TEMPLATE/bug_report.md

33-33: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


34-34: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


35-35: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


36-36: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)

🔇 Additional comments (3)
.github/ISSUE_TEMPLATE/bug_report.md (1)

28-36: Well-structured template improvements for tracking bug reports

The enhanced system setup section will be particularly helpful for capturing environment details needed to debug tracking-related issues, such as the Kalman filter problems addressed in this PR. The clear structure and specific version fields will make it easier to reproduce and resolve similar issues in the future.

🧰 Tools
🪛 Markdownlint (0.37.0)

33-33: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


34-34: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


35-35: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)


36-36: Expected: 2; Actual: 4
Unordered list indentation

(MD007, ul-indent)

sleap/version.py (1)

14-14: Consider deferring the version bump to 1.4.1

The version bump from "1.4.1a2" to "1.4.1" indicates a transition from alpha to stable release. However, this PR is actively fixing a critical Kalman filter tracking functionality. Consider:

  1. Keeping the alpha version until the tracking fixes are merged and tested
  2. Creating a separate PR for the version bump after confirming stability

Let's check if there are any other pending PRs or issues that might affect stability:

✅ Verification successful

Let me gather more information about version-related changes and recent bug fixes.


Let me check the test file and any recent changes to version-related files.


Based on the gathered information, I can now generate the final response:

Version bump to 1.4.1 appears appropriate

The version change from "1.4.1a2" to "1.4.1" is justified because:

  • No open PRs or issues with bug/critical labels exist
  • The version is widely integrated across the codebase for tracking, training, and diagnostics
  • The version format in setup.py confirms this follows the project's versioning pattern
  • The test suite includes version validation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for open PRs and issues with labels indicating bugs or critical issues
gh pr list --state open --label "bug,critical" --json title,url
gh issue list --state open --label "bug,critical" --json title,url

Length of output: 136


Script:

#!/bin/bash
# Check recent commits and PR description for context about version changes and bug fixes
gh pr view --json title,body,commits

# Check for any changelog or release notes
fd "CHANGELOG|HISTORY|RELEASES" --type f

# Look for any version-related files or tests
fd "version|release" --type f

Length of output: 204


Script:

#!/bin/bash
# Check version test file content
cat tests/test_version.py

# Check git history for version.py to understand recent changes
git log -n 5 --oneline -- sleap/version.py

# Look for any version imports or usage in the codebase
rg "__version__" --type py -C 2

Length of output: 5345

docs/conf.py (1)

181-181: LGTM: Minor style update

The change from single to double quotes in html_css_files is consistent with Python style guides.

Comment on lines 30 to 31
- Version(s):
<!-- e.g. [SLEAP v1.4.1, python 3.8] --->
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix HTML comment syntax in version example

The HTML comment in the version example uses incorrect syntax for closing the comment.

Apply this change:

-<!-- e.g. [SLEAP v1.4.1, python 3.8] --->
+<!-- e.g. [SLEAP v1.4.1, python 3.8] -->
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Version(s):
<!-- e.g. [SLEAP v1.4.1, python 3.8] --->
- Version(s):
<!-- e.g. [SLEAP v1.4.1, python 3.8] -->

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
README.rst (1)

72-72: Reconsider adding dev channel to main installation instructions.

While this change helps users access the Kalman filter fix, adding a development channel to the main installation instructions may expose users to unstable features. Consider:

  1. Adding a note explaining why the dev channel is temporarily needed
  2. Documenting when this fix will be available in the stable channel
  3. Moving this to a separate "Development Version" section

Here's a suggested approach:

- conda create -y -n sleap -c conda-forge -c nvidia -c sleap/label/dev -c sleap -c anaconda sleap
+ conda create -y -n sleap -c conda-forge -c nvidia -c sleap -c anaconda sleap

+ Development Version
+ ^^^^^^^^^^^^^^^^^
+ To access the latest fixes (including Kalman filter improvements), use:
+ 
+ .. code-block:: bash
+
+     conda create -y -n sleap -c conda-forge -c nvidia -c sleap/label/dev -c sleap -c anaconda sleap
+
+ Note: The development version may include unstable features. These fixes will be available
+ in the next stable release (v1.4.2).
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e260ae and e68928f.

📒 Files selected for processing (2)
  • README.rst (1 hunks)
  • docs/installation.md (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/installation.md

@eberrigan eberrigan force-pushed the elizabeth/Set-Kalman-filter-args-equal-to-max-tracking-args branch from e68928f to 32aa5ee Compare December 19, 2024 17:30
@eberrigan eberrigan force-pushed the elizabeth/Set-Kalman-filter-args-equal-to-max-tracking-args branch from 32aa5ee to fa5a82b Compare December 19, 2024 17:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
sleap/nn/tracking.py (4)

827-835: Use logging instead of print statement.

Replace the print statement with proper logging for better control over log levels and output.

Apply this change:

-            print("Connecting single track breaks.")
+            logger = logging.getLogger(__name__)
+            logger.info("Connecting single track breaks.")

Line range hint 931-951: Document GUI limitations in docstring.

The comment indicates GUI accessibility limitations. This should be properly documented in the method's docstring.

Add this to the method's docstring:

"""
Notes:
    The pre-culling functionality using target_instance_count is currently not 
    accessible through the GUI. Use max_tracks parameter instead when working 
    with the GUI.
"""

Line range hint 955-991: Improve error messages for Kalman filter requirements.

The error messages could be more descriptive to help users understand the requirements and alternatives.

Apply these changes:

-                raise ValueError(
-                    "Kalman filter requires node indices for instance tracking."
-                )
+                raise ValueError(
+                    "Kalman filter requires node indices for instance tracking. "
+                    "Please specify node_indices parameter."
+                )

-                raise ValueError(
-                    "Kalman filter requires simple tracker for initial tracking."
-                )
+                raise ValueError(
+                    "Kalman filter requires simple tracker for initial tracking. "
+                    "Please use 'simple' or 'simplemaxtracks' tracker instead of flow-based trackers."
+                )

-                raise ValueError(
-                    "Kalman filter does not support normalized_instance_similarity."
-                )
+                raise ValueError(
+                    "Kalman filter does not support normalized_instance_similarity. "
+                    "Please use a different similarity function."
+                )

-            raise ValueError(
-                "Kalman filter requires max tracks or target instance count."
-            )
+            raise ValueError(
+                "Kalman filter requires either max_tracks or target_instance_count parameter. "
+                "Please specify one of these parameters."
+            )

1405-1407: Use logging instead of print statement for Kalman filter initialization.

Replace the print statement with proper logging for better control over log levels and output.

Apply this change:

-        print(
-            f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters."
-        )
+        logger = logging.getLogger(__name__)
+        logger.info(
+            f"Using {init_tracker.get_name()} to track {init_frame_count} frames for Kalman filters."
+        )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e68928f and fa5a82b.

📒 Files selected for processing (5)
  • docs/guides/cli.md (2 hunks)
  • sleap/nn/inference.py (2 hunks)
  • sleap/nn/tracker/kalman.py (1 hunks)
  • sleap/nn/tracking.py (9 hunks)
  • tests/nn/test_tracking_integration.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • sleap/nn/tracker/kalman.py
  • tests/nn/test_tracking_integration.py
  • docs/guides/cli.md
🔇 Additional comments (2)
sleap/nn/tracking.py (1)

Line range hint 5722-5725: LGTM: Standard main module check implementation.

The main module check is correctly implemented using the standard Python idiom.

sleap/nn/inference.py (1)

1132-1136: LGTM: Correct implementation of conditional unragging.

The conditional unragging of outputs is correctly implemented using a lambda function.

@@ -574,7 +574,7 @@ class Tracker(BaseTracker):
max_tracking: bool = False # To enable maximum tracking.

cleaner: Optional[Callable] = None # TODO: deprecate
target_instance_count: int = 0
target_instance_count: int = 0 # TODO: deprecate
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement proper deprecation warning for target_instance_count.

Since target_instance_count is marked for deprecation but still being used, implement a proper deprecation warning to notify users.

Add this warning in the __init__ method:

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    if self.target_instance_count:
        import warnings
        warnings.warn(
            "target_instance_count is deprecated and will be removed in a future version. "
            "Use max_tracks instead.",
            DeprecationWarning,
            stacklevel=2
        )

@eberrigan
Copy link
Contributor Author

Closes #1980

@eberrigan eberrigan merged commit 66d96ce into develop Dec 19, 2024
15 of 16 checks passed
@eberrigan eberrigan deleted the elizabeth/Set-Kalman-filter-args-equal-to-max-tracking-args branch December 19, 2024 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants