Skip to content

Conversation

@maximpavliv
Copy link
Contributor

@maximpavliv maximpavliv commented Jun 16, 2025

This PR continues the work started in #121, which was left incomplete.

Specifically:

Remaining work:

  • get_system_info() in dlclive/benchmark.py uses torch to get GPU data. We want an implementation that uses the library corresponding to the model in use (tf or torch).
  • Add pytorch models to dlclive/check_install/check_install.py , benchmarking/run_dlclive_benchmark.py, and tests/test_benchmark_script.py - once pytorch models have been selected and uploaded online!

Then, #121 will be ready for merge into main

MMathisLab and others added 16 commits June 13, 2025 16:15
- changed benchmarking link; needs testing
* Update .gitignore

* CI/CD update python version

* CI/CD update actions versions

* CI/CD update trigger events

* CI/CD update MacOS version

* dlclibrary set version to >=0.0.6

* Poetry lock

* Poetry lock

* Pyproject.toml update tensorflow installation

* Poetry lock

* Install specific tensorflow-io-gcs-filesystem for windows

* Poetry lock

* Update deprecated section name

* Poetry lock

* CI/CD test on python 3.11, 3.12, 3.13 as well

* Update testing.yml

- rename to main
- add tables installation

---------

Co-authored-by: Mackenzie Mathis <mathis@rowland.harvard.edu>
* Update README.md

fix test instructions

* Update check_install.py

* Update check_install.py

* Update check_install.py
* Update pyproject.toml

- adding support for python 3.11, 3.12, 3.13

* Update testing.yml

* Update testing.yml

* Update pyproject.toml

* Update testing.yml

* Update pyproject.toml

* Update poetry.lock

* Update poetry.lock

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update testing.yml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update testing.yml

* Update pyproject.toml

* Update pyproject.toml

* Update poetry.lock

* Update testing.yml

* Update testing.yml

* Update testing.yml

- remove poetry (for now)

* Update testing.yml

* Update testing.yml

* Update testing.yml

* Update testing.yml

* Update testing.yml

* updated poetry.lock

* updated

* Update testing.yml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update testing.yml

testing 3.11

* Update testing.yml

* Update pyproject.toml

* Update pyproject.toml

* Update pyproject.toml

* Update testing.yml

* updated test for win32

* Update testing.yml

* Update testing.yml

* Update testing.yml

* Update testing.yml
@maximpavliv maximpavliv changed the title [WIP] Continue work on _Integration with DeepLabCut 3.0_ [WIP] Continue work on Integration with DeepLabCut 3.0 Jun 16, 2025
@MMathisLab MMathisLab requested a review from Copilot January 13, 2026 11:53
@MMathisLab MMathisLab merged commit ac75b2a into dlclive3 Jan 13, 2026
6 of 7 checks passed
@MMathisLab MMathisLab deleted the maxim/dlclive3 branch January 13, 2026 11:54
Copy link

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 continues the integration of DeepLabCut 3.0 with PyTorch support, merging the main branch and consolidating three separate benchmarking modules into a single unified module that works with both TensorFlow and PyTorch backends.

Changes:

  • Merged benchmark_tf.py and benchmark_pytorch.py into a single benchmark.py module supporting both engines
  • Added Engine enum class to detect and classify model types (TensorFlow vs PyTorch)
  • Updated factory pattern and check_install scripts to use the new Engine detection
  • Fixed PyTorch runner bug where empty detection batches caused issues

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 27 comments.

Show a summary per file
File Description
dlclive/engine.py New Engine enum for TensorFlow/PyTorch model type detection
dlclive/benchmark.py Unified benchmarking module supporting both TF and PyTorch
dlclive/benchmark_tf.py Removed - merged into benchmark.py
dlclive/benchmark_pytorch.py Removed - merged into benchmark.py
dlclive/factory.py Updated to use Engine enum for model type detection
dlclive/pose_estimation_pytorch/runner.py Fixed empty detection batch handling
dlclive/check_install/check_install.py Updated to support both engine types
dlclive/init.py Added exports for benchmark functions
tests/test_benchmark_script.py New functional test for benchmarking
pytest.ini Added pytest configuration with functional marker
pyproject.toml Updated dependencies, moved opencv-python-headless, added pytest
benchmarking/run_dlclive_benchmark.py Updated to use Engine detection
README.md Updated test instructions
.gitignore Added __MACOSX exclusion
.github/workflows/testing.yml Updated CI to support conda and both engines
.github/workflows/python-package.yml Updated branch references from master to main
Comments suppressed due to low confidence (2)

dlclive/benchmark.py:868

    benchmark_videos(
        video_path=args.video_path,
        model_path=args.model_path,
        model_type=args.model_type,
        device=args.device,
        precision=args.precision,
        display=args.display,
        pcutoff=args.pcutoff,
        display_radius=args.display_radius,
        resize=tuple(args.resize) if args.resize else None,
        cropping=args.cropping,
        dynamic=tuple(args.dynamic),
        save_poses=args.save_poses,
        save_dir=args.save_dir,
        draw_keypoint_names=args.draw_keypoint_names,
        cmap=args.cmap,
        get_sys_info=args.get_sys_info,
        save_video=args.save_video,
    )

dlclive/check_install/check_install.py:13

  • This import of module urllib.request is redundant, as it was previously imported on line 9.
import urllib.request

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

pandas = ">=1.0.1,!=1.5.0"
tables = "^3.8"
opencv-python-headless = "^4.5"
pytest = "^8.0"
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

Adding pytest as a regular dependency (not a dev dependency) is unusual. Test frameworks are typically dev dependencies. Consider moving pytest to [tool.poetry.group.dev.dependencies] instead.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +15
dog_models = glob.glob(str(datafolder / "dog" / "*[!avi]"))
dog_video = glob.glob(str(datafolder / "dog" / "*.avi"))[0]
mouse_models = glob.glob(str(datafolder / "mouse_lick" / "*[!avi]"))
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

The glob pattern "*[!avi]" doesn't correctly exclude .avi files. This pattern matches any path that doesn't end with 'i' (just the last character). To properly exclude .avi files, use a more explicit approach like filtering the results or using a different pattern like "*/[!.]*.pb" for TensorFlow models or "*/*.pt" for PyTorch models.

Suggested change
dog_models = glob.glob(str(datafolder / "dog" / "*[!avi]"))
dog_video = glob.glob(str(datafolder / "dog" / "*.avi"))[0]
mouse_models = glob.glob(str(datafolder / "mouse_lick" / "*[!avi]"))
dog_models = [p for p in glob.glob(str(datafolder / "dog" / "*")) if not p.lower().endswith(".avi")]
dog_video = glob.glob(str(datafolder / "dog" / "*.avi"))[0]
mouse_models = [p for p in glob.glob(str(datafolder / "mouse_lick" / "*")) if not p.lower().endswith(".avi")]

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +34
result = benchmark_videos(
model_path=model_path,
model_type="base" if Engine.from_model_path(model_path) == Engine.TENSORFLOW else "pytorch",
video_path=dog_video,
output=str(out_dir),
n_frames=n_frames,
pixels=pixels
)
print("Dog model result:", result)
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

The benchmark_videos() function doesn't return a value according to its implementation (it only saves results to disk when output is provided), but the test stores and prints the return value. This could lead to confusion. Either remove the assignment and print statements, or check the actual output files instead.

Copilot uses AI. Check for mistakes.

# make temporary directory in $HOME
# TODO: why create this temp directory in $HOME?
# make temporary directory in $current
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

The comment says "make temporary directory in $current" but the code actually creates it in the home directory (Path().home() / "dlc-live-tmp"). Update the comment to accurately reflect where the directory is created, or change the code to match the comment.

Suggested change
# make temporary directory in $current
# make temporary directory in the user's home directory

Copilot uses AI. Check for mistakes.
Comment on lines +152 to +159
video_path = video_path if type(video_path) is list else [video_path]

# fix resize
if pixels:
pixels = pixels if type(pixels) is list else [pixels]
resize = [None for p in pixels]
elif resize:
resize = resize if type(resize) is list else [resize]
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

Using type(pixels) is list and type(resize) is list are not recommended Python practices. Use isinstance() instead for type checking.

Suggested change
video_path = video_path if type(video_path) is list else [video_path]
# fix resize
if pixels:
pixels = pixels if type(pixels) is list else [pixels]
resize = [None for p in pixels]
elif resize:
resize = resize if type(resize) is list else [resize]
video_path = video_path if isinstance(video_path, list) else [video_path]
# fix resize
if pixels:
pixels = pixels if isinstance(pixels, list) else [pixels]
resize = [None for p in pixels]
elif resize:
resize = resize if isinstance(resize, list) else [resize]

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,48 @@
import os
import glob
import pathlib
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

Import of 'pathlib' is not used.

Suggested change
import pathlib

Copilot uses AI. Check for mistakes.
"""
Downloads and extracts DeepLabCut-Live benchmarking data (videos & DLC models).
"""
import os
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

This import of module os is redundant, as it was previously imported on line 757.

Suggested change
import os

Copilot uses AI. Check for mistakes.
Comment on lines +657 to +676
except:
fourcc = ""
try:
fps = round(cap.get(cv2.CAP_PROP_FPS))
except:
fps = None
try:
pix_fmt = decode_fourcc(cap.get(cv2.CAP_PROP_CODEC_PIXEL_FORMAT))
except:
pix_fmt = ""
try:
frame_count = round(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except:
frame_count = None
try:
orig_im_size = (
round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
)
except:
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

Except block directly handles BaseException.

Suggested change
except:
fourcc = ""
try:
fps = round(cap.get(cv2.CAP_PROP_FPS))
except:
fps = None
try:
pix_fmt = decode_fourcc(cap.get(cv2.CAP_PROP_CODEC_PIXEL_FORMAT))
except:
pix_fmt = ""
try:
frame_count = round(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except:
frame_count = None
try:
orig_im_size = (
round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
)
except:
except Exception:
fourcc = ""
try:
fps = round(cap.get(cv2.CAP_PROP_FPS))
except Exception:
fps = None
try:
pix_fmt = decode_fourcc(cap.get(cv2.CAP_PROP_CODEC_PIXEL_FORMAT))
except Exception:
pix_fmt = ""
try:
frame_count = round(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except Exception:
frame_count = None
try:
orig_im_size = (
round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
)
except Exception:

Copilot uses AI. Check for mistakes.
Comment on lines +657 to +676
except:
fourcc = ""
try:
fps = round(cap.get(cv2.CAP_PROP_FPS))
except:
fps = None
try:
pix_fmt = decode_fourcc(cap.get(cv2.CAP_PROP_CODEC_PIXEL_FORMAT))
except:
pix_fmt = ""
try:
frame_count = round(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except:
frame_count = None
try:
orig_im_size = (
round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
)
except:
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

Except block directly handles BaseException.

Suggested change
except:
fourcc = ""
try:
fps = round(cap.get(cv2.CAP_PROP_FPS))
except:
fps = None
try:
pix_fmt = decode_fourcc(cap.get(cv2.CAP_PROP_CODEC_PIXEL_FORMAT))
except:
pix_fmt = ""
try:
frame_count = round(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except:
frame_count = None
try:
orig_im_size = (
round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
)
except:
except Exception:
fourcc = ""
try:
fps = round(cap.get(cv2.CAP_PROP_FPS))
except Exception:
fps = None
try:
pix_fmt = decode_fourcc(cap.get(cv2.CAP_PROP_CODEC_PIXEL_FORMAT))
except Exception:
pix_fmt = ""
try:
frame_count = round(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except Exception:
frame_count = None
try:
orig_im_size = (
round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
)
except Exception:

Copilot uses AI. Check for mistakes.

return inf_times, im_size, meta
os.makedirs(os.path.normpath(output), exist_ok=True)
pickle.dump(data, open(out_file, "wb"))
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

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

File is opened but is not closed.

Suggested change
pickle.dump(data, open(out_file, "wb"))
with open(out_file, "wb") as f:
pickle.dump(data, f)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants