Repository navigation
Fix adapt_labeled_data_to_new_project crash and labeled-data folder lookup - #3532
MohammadHijjawi97 wants to merge 1 commit into
Conversation
adapt_labeled_data_to_new_project raised "AttributeError: type object 'tqdm' has no attribute 'tqdm'" on its first line of work, because the module imports the tqdm class rather than the module. Call tqdm directly. The labeled-data folder name was also derived by splitting the video path on "\\" only, so for forward-slash paths (every Linux/macOS project) the whole path was joined onto labeled-data and the folder was not found. Use PureWindowsPath(video).stem, which accepts both separators and matches the folder names DeepLabCut creates (Path(video).stem), including video names that contain dots.
There was a problem hiding this comment.
This one will need a decision from other devs; I think we should remove the function.
This has no callers, no init and was always just stranded on its own.
While the feature shape overall is useful in itself, I think the current way it is designed and the conflict with our WIP I/O refactor & data validation system, plus the fact that it was never reachable, have me lean towards removing this function.
An example of one the risks enabling this exposes: with remove_old_bodyparts=True on a single-animal project (multianimalbodyparts == []), it drops every column and writes back over the CSV. This is not reachable today either, once we merge this it would be.
None of this is the PR's fault either, it's just previous dead code we missed.
| video_name = video_name.split(".")[0] | ||
| # PureWindowsPath accepts both "\\" and "/" separators, so the video name is | ||
| # found regardless of the OS the project was created on. | ||
| video_name = PureWindowsPath(video).stem |
There was a problem hiding this comment.
Context: this is a rogue surviving pattern from #3421 . I would recommend handling it with Path.stem to avoid creating a special case here (if we end up keeping the func)
There was a problem hiding this comment.
A note about this file: the PR that added it never added the required imports.
It's only the ruff pass that added them, and for tqdm it added the wrong one, hence the failure. But this function was never used or usable anywhere, this actually enables it for the first time
|
Thanks @C-Achard, that makes sense. The |
There was a problem hiding this comment.
@C-Achard I agree that we should remove this function. It is outdated and risky, and not used in the codebase. It is exported under deeplabcut.utils, so technically users may use it outside of our codebase. But I doubt that people actually used it, and is has been broken for a while anyway. Since the napari-deeplabcut updates, the user-case (moving labeled data to a new project) can be solved more easily anyway, so I agree that the function is not needed.
@MohammadHijjawi97 thanks for your contribution, your PR definitely improved the code, but as discussed I think the function still poses risks instead of solving problems. You can open a replacement PR if you are interested to delete the function. Or we can take care of it.
After the function is removed, we can close this PR.
|
Thanks both. I opened #3543, which removes |
The function had no callers and could not run (tqdm.tqdm on the tqdm class). Per maintainer discussion in #3532 it is removed rather than fixed. Also drops the numpy, tqdm and deeplabcut imports that only it used.
|
All good, #3543 is now being merged, so I will close this one. Thanks again! |
* Remove empty pytorch benchmark module * Stabilize randomized PyTorch tests Replace random test inputs with deterministic fixtures and stable pytest ids across the pose-estimation PyTorch tests. This avoids run-after-discovery desync, which makes the tests impossible to run in e.g. the VSCode pytest extension * Diversify PyTorch test coverage Assign per-case RNG seeds to the PyTorch pose estimation tests and replace a few random samples with fixed edge-case parameters. This keeps test IDs stable while covering a wider range of augmentation, batch size, and input-size combinations. * Update test_api_utils.py * Fix ratio kwarg Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix uv package upgrades in GUI updater The GUI updater was invoking `uv pip install -U ...`, which can upgrade the wrong packages when requirements include extras. This patch strips extras from package specs and uses `--upgrade-package` with the canonical distribution names, preserving the requested package set while keeping the pip fallback intact. It also adds regression coverage for the uv-only and uv-then-pip command-building paths. * Bump urllib3 from 2.7.0 to 2.8.0 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.7.0 to 2.8.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](urllib3/urllib3@2.7.0...2.8.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.8.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * Remove unused adapt_labeled_data_to_new_project from conversioncode The function had no callers and could not run (tqdm.tqdm on the tqdm class). Per maintainer discussion in #3532 it is removed rather than fixed. Also drops the numpy, tqdm and deeplabcut imports that only it used. * fix unfitted ellipse tracking * test also unfitted ellipse track with identities * Expose DataLoader persistent_workers and prefetch_factor in train_settings Training on small datasets over hundreds of epochs respawns DataLoader workers every epoch (persistent_workers=False) with a fixed prefetch depth of 2, leaving GPU utilization on the table for CPU-heavy pipelines. These knobs existed in PyTorch but were not reachable through TrainSettingsConfig, pytorch_config.yaml, or pytorch_cfg_updates (strict schema rejects them). - TrainSettingsConfig: add dataloader_persistent_workers (default False) and dataloader_prefetch_factor (default None = PyTorch default 2, validated >= 1). - train(): forward both to the training DataLoader when dataloader_workers > 0; warn and ignore them for single-process loading (PyTorch requires workers for both options). .get() with defaults keeps old persisted configs without the keys working. - base.yaml / base_detector.yaml defaults + pytorch_config.md docs. Adds config roundtrip/validation tests and train() DataLoader-kwarg tests. Addresses #3547 * fixup: StrictPositiveInt, shared dataloader-kwargs helper, compat test * Add contribution requirements to AGENTS This change adds a Contributing section to AGENTS.md documenting new human review disclosure, issue/PR checks, dev setup commands, and testing expectations for DeepLabCut contributions. * Bump werkzeug from 3.1.8 to 3.1.9 Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.1.8 to 3.1.9. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](pallets/werkzeug@3.1.8...3.1.9) --- updated-dependencies: - dependency-name: werkzeug dependency-version: 3.1.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * add reject_reserved_keys validator * revert Dataloader througput settings Reverts acbf42a and 658becf. * add TrainSettingsConfig.dataloader_kwargs * Add missing runner config field * Bump uv to 0.13.4 and certifi to 2026.10.19 (emSign/eMudhra CA incident) * Bump soupsieve from 2.8.4 to 2.9 Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.8.4 to 2.9. - [Release notes](https://github.com/facelessuser/soupsieve/releases) - [Commits](facelessuser/soupsieve@2.8.4...2.9) --- updated-dependencies: - dependency-name: soupsieve dependency-version: '2.9' dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> * AGENTS.md: remove duplicate text * point the collate_fn hint at data.train.collate The training loader reads run_config["data"]["train"]["collate"], so the hint sent users to a key the config does not have. * Bump anyio from 4.13.0 to 4.14.2 (#3523) Bumps [anyio](https://github.com/agronholm/anyio) from 4.13.0 to 4.14.2. - [Release notes](https://github.com/agronholm/anyio/releases) - [Commits](agronholm/anyio@4.13.0...4.14.2) --- updated-dependencies: - dependency-name: anyio dependency-version: 4.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cyril Achard <cyril.achard@epfl.ch> * Docker: require pip>=26.2.2 and certifi>=2026.10.19, refresh ca-certificates (#3561) * Update AGENTS.md Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> * Update AGENTS.md * Apply suggestion from @deruyter92 * Update AGENTS.md Co-authored-by: Cyril Achard <cyril.achard@epfl.ch> * Bump gitpython from 3.1.59 to 3.1.62 (#3542) Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.59 to 3.1.62. - [Release notes](https://github.com/gitpython-developers/GitPython/releases) - [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES) - [Commits](gitpython-developers/GitPython@3.1.59...3.1.62) --- updated-dependencies: - dependency-name: gitpython dependency-version: 3.1.62 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cyril Achard <cyril.achard@epfl.ch> * Bump tornado from 6.5.8 to 6.5.9 (#3540) Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.5.8 to 6.5.9. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](tornadoweb/tornado@v6.5.8...v6.5.9) --- updated-dependencies: - dependency-name: tornado dependency-version: 6.5.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Cyril Achard <cyril.achard@epfl.ch> * Seek to the first frame in `KmeansbasedFrameselectioncv2` for consecutive indices (#3531) * Seek to the first frame in KmeansbasedFrameselectioncv2 for consecutive indices When the candidate frame indices are consecutive (e.g. extract_frames with algo="kmeans" and the default cluster_step=1), frames were read sequentially from the current reader position without seeking first. With start > 0 the clustering therefore ran on the first frames of the video instead of the requested [start, stop] range, while the returned indices still pointed into that range. Seek to the first index before reading. * VideoReader tests: move `FakeVideoReader` to conftest.py * VideoReader tests: add test for extract outlier frames path --------- Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> * Update tests/pose_estimation_pytorch/other/test_dataset.py * Fix metadata generation for portable pickling in video API (#3564) * Refactor pickle loading to handle paths from different operating systems (#3565) * Add version-aware known_defect pytest marker (#3526) Centralize known-defect version bounds in tests/conftest.py. The known_defect marker applies a strict AssertionError-only xfail through the registered version and rejects unknown defect names during collection * Add regression tests for dataset enumeration (#3527) * Add regression tests for dataset enumeration Add shared training-dataset fixtures and strict-xfail coverage for labeled-data enumeration edge cases. The new tests document and pin bugs around `_labeled` folder filtering, substring-based stem removal, and unfiltered labeled-data merges, while also adding a control case and clarifying known-defect metadata in `tests/conftest.py`. * Add missing video declaration - The fixture's video_sets is empty, so session-01 was not named by the config either; only the backup folder should be unconfigured - A fix that merges only folders named in video_sets would have returned no frames, and pd.concat would raise ValueError instead of the test passing, making the fix look like a regression * Adapt versions for tests (require fixed in 3.1.0) * `InferenceRunner` + `analyze_videos`: Fix top-down analysis crashing for zero-detections and unreadable video handling (#3488) * fix _generate_output_data crash on empty predictions Handle the valid case where PyTorch video inference returns zero predictions. * Allow create_df_from_prediction() to handle an empty predictions list. videos with zero detections would fail after serialization on `np.stack([])` Return an empty DataFrame with the expected DLC column schema instead and add a regression test for the empty-output case. * Treat zero predictions as a distinct warning case in `video_inference()` * Don't write dataframe for empty predictions - add informative warning - add guard in dataframe export: raise ValueError if no predictions * update test_videos: pop `"PAFinds"` before comparison * update test_videos: assert no DF is written for empty predictions * Return empty-detection inference results Ensure inference emits results for images whenever a prediction exists, even when no detections are produced and thus no queue item is generated. This avoids dropping frames with valid predictions but empty detection outputs. * Use CTD predictions for video output Fixes a mismatch in `analyze_videos` where the DataFrame export step still used `predictions` instead of `ctd_predictions`. This ensures saved video analysis results use the corrected prediction set from the current processing path. * Handle empty prediction outputs gracefully When a video produces no pose predictions, analysis now writes all-NaN placeholder predictions so downstream HDF5 export and filtering can continue instead of failing on an empty result set. The DataFrame export path was adjusted to handle these fallback frames correctly and raises clearer errors only for genuinely invalid empty input. FMPOSE 3D inference now also raises a clear RuntimeError when no 2D poses are detected for a video. * Reuse shared frame key width helper Move frame key zero-padding logic into `runners.shelving.frame_key_width` and use it from both video output generation and shelf writing. This keeps frame key formatting consistent across full-data exports and on-the-fly shelving, while preserving the default width when the frame count is unknown. * Clarify video inference warnings Refines `video_inference` logging so empty results distinguish unreadable videos from detector misses, and keeps re-encoding guidance for partial reads. Expands tests to cover zero-prediction, partial-prediction, and shelf-writer cases. * Go back to ValueError for empty predictions * Remove NaN fallback for empty video predictions Simplifies `analyze_videos` by always passing the inference `predictions` directly to `create_df_from_prediction` when `save_as_df` is enabled, and removes the special-case branch that generated all-NaN placeholder rows when no predictions were produced. The unused `_nan_predictions` helper and its warning/error handling were also deleted. * Fix CTD export guard for empty predictions Use the CTD prediction list when deciding whether to write the dataframe, so `.h5` export still happens based on the converted CTD results instead of an unrelated variable. The warning now also explains that the CTD pickle had no frames when export is skipped. * Clarify empty CTD prediction handling Add an explanatory comment in `analyze_videos` documenting why `ctd_predictions` can be empty, and why the code warns and skips dataframe creation instead of raising. This captures edge cases like zero-frame pickle output and `save_as_df=False`. * Centralize test fakes * Add test for async with no images * Clarify async inference test comment Refines the test docstring in `test_runners_inference.py` to describe the async behavior more clearly by comparing it to the sequential path without implying a regression pin. * Use shared frame key width helper ShelfWriter now always derives its frame key width through `frame_key_width()` instead of keeping a separate default. This removes duplicated fallback logic and keeps frame index formatting consistent with the shared helper. * Don't write pickle for unreadable video All readable frames yield a prediction. An empty list of predictions means the video could not be read, and no pickle should be saved: it would be detected as analyzed. * Video inference: update warning for empty predictions since #3488, all readable frames yield a prediction (even when no animal was detectied) an empty predictions means no readable frames. * InferenceRunner: don't emit results for producer exceptions * update code comment * fix ShelfWriter: report zero framens when empty * Skip unreadable videos with shelf writer + improve docstring and log message * Fix warning typo + update tests * Remove unreachable CTD empty-export branch and reword FMPose error --------- Co-authored-by: C-Achard <cyril.achard@epfl.ch> * Bump pillow from 12.2.0 to 12.3.0 (#3563) Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.2.0 to 12.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](python-pillow/Pillow@12.2.0...12.3.0) --- updated-dependencies: - dependency-name: pillow dependency-version: 12.3.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Restrict filterpy version to <2 * Update default Python version to 3.12 in workflow configurations (#3568) Bump the dev docs workflow python version to 3.12 to avoid failure on 3.10 inompcatible pymdown/mkdocstrings pair being installed in CI. Code is parsed, not imported, so this would not hide failures from using old python patterns or 3.13+ only-patterns (other jobs would catch it ideally), so there should be no immediate downside to the bump. Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> * replace crashing monkeypatch in pd options (#3571) * Bump pyasn1, oauthlib, gitpython and filelock - Combines dependabot security updates #3572, #3573, #3574 and #3575, rebuilt on dev * Omit `dataloader_kwargs` from `TrainSettingsConfig` if unset to ensure forward compatibility (#3576) * add model_serializer for TrainSettingsConfig.dataloader_kwargs omit dataloader_kwargs in the .to_dict, .to_yaml and to_commented_map views when the field is not configured (empty or None) This keeps forward compatibility in older deeplabcut versions * dataset_kwargs: Use pydantic 2.11 `exclude_if` instead of model serializer * update pyproject.toml + uv lock: require pydantic 2.11 * Update pyproject.toml * update uv.lock * Fix GUI freeze when showing trajectory plots in Analyze Videos (#3466) * Run trajectory plots after analysis completes Store plotting options before starting the worker and defer `plot_trajectories(showfigures=True)` to a main-thread finished slot. The analysis pipeline now always runs with `showfigures=False`, only opens trajectory plots when analysis succeeds, and centralizes button/progress-bar reset in the completion handler. * Fix trajectory plot timing with deferred timer Refactor post-analysis trajectory plot display to use a single-shot QTimer, ensuring the UI (button re-enable, progress bar hide) fully updates before plots are shown. Also fixes the `_handle_analysis_error` slot signature to accept the error argument, and adds error handling around trajectory plot display. * Avoid duplicate trajectory plotting in GUI Update the Analyze Videos tab so `deeplabcut.plot_trajectories(...)` only runs when `plot_trajectories` is enabled and `show_trajectory_plots` is not. This prevents triggering the plotting routine in cases where trajectory plots are already being shown through the separate display flow. * Refactor pending plot cleanup logic Extract repeated None-assignment into `_clear_pending_trajectory_plots()` helper. Also move the button re-enable and progress bar hide after the plot timer/clear logic to ensure UI updates happen regardless of plot path. * Refactor: Each batch now saves its trajectory plots to disk as it finishes --------- Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> Co-authored-by: Mackenzie Weygandt Mathis <mathis@rowland.harvard.edu> * Add Cookbook recipe: fish swimming kinematics from DeepLabCut (#3398) * Add Cookbook recipe: fish swimming kinematics from DeepLabCut Adds a post-processing recipe showing how to derive swimming-kinematics metrics (tail-beat frequency, swimming speed, body curvature, diel index) from single-animal DeepLabCut pose tracks, and how to use the Spearman-Brown reliability index to choose a reliable observation duration. Registers the page in the docs table of contents. Adapted from Hwang et al. 2026, Ecological Informatics 95, 103760. * move fish kinematics recipe to dlc-utils community contributions --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Mackenzie Weygandt Mathis <mathis@rowland.harvard.edu> Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Juan Cobos <juan.cobos@igf.cnrs.fr> Co-authored-by: Cyril Achard <cyril.achard@epfl.ch> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Mohammad Hijjawi <mohammad.hijjawi1997@gmail.com> Co-authored-by: lu123bing <12312702@mail.sustech.edu.cn> Co-authored-by: Jaap de Ruyter <deruyter92@gmail.com> Co-authored-by: Jaap de Ruyter van Steveninck <32810691+deruyter92@users.noreply.github.com> Co-authored-by: Mohammad Hijjawi <51020382+MohammadHijjawi97@users.noreply.github.com> Co-authored-by: Mackenzie Weygandt Mathis <mathis@rowland.harvard.edu> Co-authored-by: Sungjoo(Dennis) Hwang <48212469+Denny-Hwang@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
What changed and why
deeplabcut.utils.conversioncode.adapt_labeled_data_to_new_projectis used to add new bodyparts to existing annotations (see the discussion in #2723). It currently fails because of two independent bugs:AttributeError: type object 'tqdm' has no attribute 'tqdm'. The module doesfrom tqdm import tqdm, but the loop calledtqdm.tqdm(...). It now callstqdm(...).video.split("\\")[-1].split(".")[0]. For forward-slash paths, i.e. every project created on Linux/macOS, no split happens, so the whole video path was joined ontolabeled-data/.pathlibthen discards the project path, and the folder is not found (FileNotFoundError).The folder name is now
PureWindowsPath(video).stem, which accepts both/and\separators. It also matches the folder names DeepLabCut creates (Path(video).stem, as inget_labeled_data_folderandconvertcsv2h5), including video names that contain dots.Related issues
Related to #2723, where this function was suggested for adding bodyparts to a labeled dataset. There is no existing issue for the crash itself.
Tests
Added
test_adapt_labeled_data_to_new_project_adds_new_bodypartstotests/test_conversioncode.py. It builds a small synthetic multi-animal project with labels fornose/tail, addsearto the config and runs the function. It then checks thatearcolumns are added as NaN for each individual, and that the existing labels are preserved. It is parametrised over a POSIX and a Windows video path. Both cases fail on the current code (AttributeError). With only the tqdm fix, the POSIX case still fails; with this PR, both pass.pytest tests/test_conversioncode.py::test_adapt_labeled_data_to_new_project_adds_new_bodyparts: 2 passedruff check/ruff format --checkon changed files: clean