Skip to content

Tracking feature : CoTracker integration for automated label generation#155

Draft
C-Achard wants to merge 27 commits intoDeepLabCut:mainfrom
C-Achard:cy/tracking-finalized
Draft

Tracking feature : CoTracker integration for automated label generation#155
C-Achard wants to merge 27 commits intoDeepLabCut:mainfrom
C-Achard:cy/tracking-finalized

Conversation

@C-Achard
Copy link
Collaborator

@C-Achard C-Achard commented Jan 16, 2026

This pull request introduces a new experimental point-tracker-assisted labeling feature to the napari-deeplabcut plugin, allowing users to semi-automate keypoint annotation across video frames. It also adds comprehensive infrastructure and tests for tracking controls and worker logic, including a dummy tracker for unit testing. The plugin's configuration and documentation are updated to reflect these enhancements.

Tracking Feature Implementation

  • Added a new "Tracking controls" widget to the plugin, enabling point tracking on keypoint layers via a simple tracker interface. This includes UI controls, key bindings, and workflow description in the documentation. [1] [2] [3]
  • Implemented core tracking data structures (TrackingWorkerData, TrackingModelInputs, RawModelOutputs, TrackingWorkerOutput) in tracking/_data.py to standardize input/output for tracking models and workers.
  • Registered the tracking widget and its controls in the plugin's configuration (napari.yaml) for proper integration into napari. [1] [2]

Testing and Infrastructure

  • Added a dummy tracker implementation and fixtures for robust unit testing of tracking workflows, including forward, backward, and both-way tracking scenarios.
  • Created extensive tests for tracking controls widget behavior and worker logic, verifying correct data propagation, progress reporting, and error handling. [1] [2]

Documentation and UI Enhancements

  • Updated the README to document the new tracking workflow, usage instructions, key bindings, and known issues for experimental point tracking.
  • Minor UI and event handling improvements in keypoint controls, including color mode visibility and layer management. [1] [2]

arashsm79 and others added 19 commits January 14, 2026 10:38
(cherry picked from commit e5d8ee6)
(cherry picked from commit 3b5780a)
(cherry picked from commit f56ee79)
(cherry picked from commit 195800b)
(cherry picked from commit d175dcb)
Renamed the trajectory plot checkbox from _matplotlib_cb to _show_traj_plot_cb in KeypointControls for clarity. Updated all references and related logic accordingly. Also improved slider maximum logic in KeypointMatplotlibCanvas and made minor comment and test cleanup.
@C-Achard C-Achard changed the title cy/tracking finalized Tracking feature : CoTracker integration for automated label generation Jan 16, 2026
@C-Achard C-Achard marked this pull request as draft January 16, 2026 09:38
@C-Achard C-Achard self-assigned this Jan 16, 2026
@C-Achard C-Achard added the enhancement New feature or request label Jan 16, 2026
@C-Achard C-Achard added this to the Tracking feature milestone Jan 16, 2026
@C-Achard C-Achard requested a review from Copilot January 16, 2026 09:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces an experimental point-tracker-assisted labeling feature that enables semi-automated keypoint annotation across video frames using CoTracker integration. The implementation includes a new tracking widget, core data structures for tracking operations, comprehensive test infrastructure with a dummy tracker, and updated documentation.

Changes:

  • Added tracking controls widget with UI for point tracking operations and key bindings
  • Implemented core tracking infrastructure (models, worker, data structures) with CoTracker 3 integration
  • Created comprehensive test suite with dummy tracker fixtures for tracking workflows
  • Updated documentation and configuration to reflect new tracking capabilities

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/napari_deeplabcut/tracking/_worker.py Implements QThread-based tracking worker with progress reporting and stop handling
src/napari_deeplabcut/tracking/_widgets.py Adds tracking controls widget with frame selection, tracking buttons, and keybindings
src/napari_deeplabcut/tracking/_models.py Defines abstract tracking model interface and CoTracker 3 implementation
src/napari_deeplabcut/tracking/_data.py Defines data structures for tracking inputs, outputs, and worker data
src/napari_deeplabcut/napari.yaml Registers new tracking controls widget in plugin configuration
src/napari_deeplabcut/_widgets.py Minor refactoring of checkbox variable names and comment updates
src/napari_deeplabcut/_tests/tracking/test_worker.py Tests for tracking worker behavior and signal emissions
src/napari_deeplabcut/_tests/tracking/test_widgets.py Tests for tracking controls widget UI and tracking requests
src/napari_deeplabcut/_tests/test_widgets.py Minor test code formatting updates
src/napari_deeplabcut/_tests/conftest.py Adds dummy tracker fixture and tracking test data fixtures
README.md Documents experimental tracking feature with usage instructions and keybindings

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Replaced print statements with logger calls in _widgets.py for better error and debug reporting. Adjusted logging level and debug flag handling in _worker.py, setting INFO as default and enabling DEBUG only when specified. Improved output validation logic in TrackingWorker.
Corrected a typo in the napari.yaml widget title ('racking' to 'tracking') and removed a redundant assignment in KeypointControls. Also cleaned up duplicate comments in the on_active_layer_change docstring.
@C-Achard C-Achard requested a review from Copilot January 16, 2026 10:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 14 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Introduces a validate_outputs method to TrackingModel as an abstract method, with concrete implementations for DummyTracker and Cotracker3. Updates tests to use output validation and improves structure checks for tracking outputs, ensuring consistency and correctness of keypoints and features.
Uncommented a debug log in _models.py and enabled conditional debugpy usage in _worker.py when DEBUG is set. Removed or updated outdated and commented-out code in _widgets.py for improved readability and maintainability.
@C-Achard C-Achard requested a review from Copilot January 16, 2026 12:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +240 to +241
if not outputs.keypoints.shape[0] == inputs.keypoints.shape[0]:
return False, "Number of output keypoints does not match number of input keypoints."
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The validation logic compares the total number of output keypoints to the number of input keypoints, but CoTracker3 outputs tracked keypoints across all frames (T * K keypoints) while inputs contain only K keypoints. This comparison will always fail for multi-frame tracking. The validation should compare outputs.keypoints.shape[0] with (T2 - T1) * K where T2 - T1 is derived from inputs.metadata['keypoint_range'].

Suggested change
if not outputs.keypoints.shape[0] == inputs.keypoints.shape[0]:
return False, "Number of output keypoints does not match number of input keypoints."
# For CoTracker3, outputs contain tracked keypoints for every frame in the
# keypoint_range. If keypoint_range = (T1, T2) and there are K input
# keypoints, we expect (T2 - T1) * K output rows.
metadata = getattr(inputs, "metadata", None)
if metadata is None or "keypoint_range" not in metadata:
return False, "Missing keypoint_range metadata required for validation."
keypoint_range = metadata["keypoint_range"]
if (
not isinstance(keypoint_range, (tuple, list))
or len(keypoint_range) != 2
):
return False, "Invalid keypoint_range metadata; expected (T1, T2)."
T1, T2 = keypoint_range
try:
T1_int = int(T1)
T2_int = int(T2)
except (TypeError, ValueError):
return False, "keypoint_range values must be integers."
if T2_int <= T1_int:
return False, "keypoint_range must satisfy T2 > T1."
K = inputs.keypoints.shape[0]
expected_n_keypoints = (T2_int - T1_int) * K
if outputs.keypoints.shape[0] != expected_n_keypoints:
return (
False,
"Number of output keypoints does not match expected "
f"((T2 - T1) * K) = {expected_n_keypoints}.",
)

Copilot uses AI. Check for mistakes.
MOVE_FORWARD_FRAME = TrackingKeybindConfig(key="i", tooltip="Move forward one frame")
MOVE_BACKWARD_FRAME = TrackingKeybindConfig(key="u", tooltip="Move backward one frame")

logger = logging.getLogger(__name__)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

Logger is defined twice in this file (lines 35 and 59). Remove the duplicate definition on line 59.

Suggested change
logger = logging.getLogger(__name__)

Copilot uses AI. Check for mistakes.
keypoints = np.array(
[
[0, 10.0, 20.0],
[1, 30.0, 40.0],
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

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

The first column of the keypoints array appears to contain sequential IDs (0, 1) rather than frame indices. Based on the comment on line 11 stating '[0]: frame number in video', both keypoints should have frame index 0 since they're query points from the reference frame. Consider changing to [[0, 10.0, 20.0], [0, 30.0, 40.0]].

Suggested change
[1, 30.0, 40.0],
[0, 30.0, 40.0],

Copilot uses AI. Check for mistakes.
@codecov-commenter
Copy link

codecov-commenter commented Jan 16, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 76.69065% with 162 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.65%. Comparing base (3a99d53) to head (b816980).
⚠️ Report is 41 commits behind head on main.

Files with missing lines Patch % Lines
src/napari_deeplabcut/tracking/_widgets.py 79.94% 76 Missing ⚠️
src/napari_deeplabcut/tracking/_models.py 45.13% 62 Missing ⚠️
src/napari_deeplabcut/_tests/conftest.py 85.71% 14 Missing ⚠️
src/napari_deeplabcut/tracking/_worker.py 87.50% 10 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #155      +/-   ##
==========================================
- Coverage   82.38%   79.65%   -2.74%     
==========================================
  Files          11       15       +4     
  Lines        1868     2831     +963     
==========================================
+ Hits         1539     2255     +716     
- Misses        329      576     +247     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +79 to +85
try:
# we let the model handle coordinate conventions internally, such that
# the worker and plugin can remain agnostic to these details.
inputs: TrackingModelInputs = model.prepare_inputs(cfg)

# Run inference; models implement their own batching and chunking
raw: RawModelOutputs = model.run(inputs, progress_callback, stop_callback)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Napari crashes (shuts down both DeepLabCut and Napari) when running line 85 on my windows machine.

[WinError 10054] An existing connection was forcibly closed by the remote host

Copy link
Collaborator

Choose a reason for hiding this comment

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

on linux, same problem, but different message:
No more messages

Copy link
Collaborator Author

@C-Achard C-Achard Jan 19, 2026

Choose a reason for hiding this comment

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

Ok, that sounds like a napari segfault.

What I usually do for these :

  1. Launch a debug script with some extra debug os env (see below)
  2. Use debugpy.debug_this_thread() above line 85 temporarily, or equivalent if you do not use debugpy.

Here is a script you can use :

"""
Simple debug script to launch napari with the tracking controls widget.
Should not be released as part of the package.
"""
import os
from napari import Viewer, run
import debugpy

os.environ["PYTHONFAULTHANDLER"] = "1"  # better segfault traces
os.environ["NAPARI_ASYNC"] = "0"  # avoid async teardown surprises 

if __name__ == "__main__":
   # DATA = your_data_path

   v = Viewer()
   keypoints_dock_widget, keypoints_plugin_widget = v.window.add_plugin_dock_widget(
       "napari-deeplabcut",
       "Keypoint controls",
   )
   tracking_dock_widget, tracking_plugin_widget = v.window.add_plugin_dock_widget(
       "napari-deeplabcut",
       "Tracking controls",
   )
   v.open(DATA, plugin="napari-deeplabcut")
   # If you want a job to be launched immediately 
   # tracking_plugin_widget._forward_spinbox_absolute.setValue(10)
   # tracking_plugin_widget._tracking_forward_button.click()
   run()
 

Let me know if that helps, these can be really tricky so definitely happy to assist further !
Also can you check if the model downloaded correctly ?

@C-Achard C-Achard added the new feature New and improved ! label Jan 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request new feature New and improved !

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants