diff --git a/deeplabcut/compat.py b/deeplabcut/compat.py index 51e8301208..491e57e313 100644 --- a/deeplabcut/compat.py +++ b/deeplabcut/compat.py @@ -603,6 +603,7 @@ def analyze_videos( identity_only: bool = False, use_openvino: str | None = None, engine: Engine | None = None, + overwrite: bool = False, **torch_kwargs, ): """Makes prediction based on a trained network. @@ -682,6 +683,8 @@ def analyze_videos( ``stitch_tracklets`` afterwards, in order to obtain the h5 file. Defaults to True. identity_only (bool, optional): If ``True`` and animal identity was learned by the model, assembly and tracking rely exclusively on identity prediction. Defaults to False. + overwrite (bool, optional): Only for the PyTorch engine. Re-analyze videos for which predictions already exist. + By default, a video whose output file is already present is skipped. Defaults to False. calibrate (bool, optional): If ``True``, use training data to calibrate the animal assembly procedure. This improves its robustness to wrong body part links, but requires very little missing data. Defaults to False. @@ -850,7 +853,7 @@ def analyze_videos( animal_names=animal_names, calibrate=calibrate, identity_only=identity_only, - overwrite=False, + overwrite=overwrite, cropping=cropping, **torch_kwargs, ) diff --git a/deeplabcut/pose_estimation_pytorch/apis/videos.py b/deeplabcut/pose_estimation_pytorch/apis/videos.py index 019103dddc..22bb990aa6 100644 --- a/deeplabcut/pose_estimation_pytorch/apis/videos.py +++ b/deeplabcut/pose_estimation_pytorch/apis/videos.py @@ -369,7 +369,9 @@ def analyze_videos( detector_batch_size: the batch size to use for detector inference. Takes the value from the project config as a default. transform: Optional custom transforms to apply to the video - overwrite: Overwrite any existing videos + overwrite: Re-analyze videos for which prediction files already exist. When + ``True`` and ``auto_track`` is enabled, existing tracklet files are also + regenerated. use_shelve: By default, data are dumped in a pickle file at the end of the video analysis. Otherwise, data are written to disk on the fly using a "shelf"; i.e., a pickle-based, persistent, database-like object by default, resulting @@ -671,7 +673,7 @@ def analyze_videos( video_extensions=video_extensions, shuffle=shuffle, trainingsetindex=trainingsetindex, - overwrite=False, + overwrite=overwrite, identity_only=identity_only, destfolder=str(output_path), snapshot_index=snapshot_index, diff --git a/tests/pose_estimation_pytorch/apis/test_compat_analyze_videos.py b/tests/pose_estimation_pytorch/apis/test_compat_analyze_videos.py new file mode 100644 index 0000000000..276d016e51 --- /dev/null +++ b/tests/pose_estimation_pytorch/apis/test_compat_analyze_videos.py @@ -0,0 +1,60 @@ +# +# DeepLabCut Toolbox (deeplabcut.org) +# © A. & M.W. Mathis Labs +# https://github.com/DeepLabCut/DeepLabCut +# +# Please see AUTHORS for contributors. +# https://github.com/DeepLabCut/DeepLabCut/blob/main/AUTHORS +# +# Licensed under GNU Lesser General Public License v3.0 +# +import inspect + +import pytest + +import deeplabcut.pose_estimation_pytorch.apis as pytorch_apis +import deeplabcut.pose_estimation_pytorch.apis.videos as videos_api +from deeplabcut.compat import Engine, analyze_videos + + +@pytest.mark.parametrize("overwrite", [True, False]) +def test_analyze_videos_forwards_overwrite(monkeypatch, overwrite): + """``overwrite`` must reach the PyTorch API rather than being pinned to False. + + Regression for #3513: a hard-coded ``overwrite=False`` plus ``overwrite`` in + ``**torch_kwargs`` used to raise ``TypeError: got multiple values for keyword + argument 'overwrite'``. Using an explicit ``overwrite`` parameter on the mock + would still raise if the wrapper double-passed the flag. + """ + captured = {} + + def fake_analyze_videos(config, *, overwrite=False, **kwargs): + captured["overwrite"] = overwrite + captured["kwargs"] = kwargs + return "mock-scorer" + + monkeypatch.setattr(pytorch_apis, "analyze_videos", fake_analyze_videos) + + result = analyze_videos( + "config.yaml", + ["video.mp4"], + engine=Engine.PYTORCH, + overwrite=overwrite, + ) + + assert result == "mock-scorer" + assert captured["overwrite"] is overwrite + assert "overwrite" not in captured["kwargs"] + + +def test_analyze_videos_auto_track_forwards_overwrite(): + """PyTorch auto_track must pass ``overwrite`` through to tracklet generation. + + Catches a hard-coded ``overwrite=False`` at the convert_detections2tracklets + call site without driving full video analysis. + """ + source = inspect.getsource(videos_api.analyze_videos) + _, _, after = source.partition("convert_detections2tracklets(") + call = after.split("stitch_tracklets(", 1)[0] + assert "overwrite=overwrite" in call + assert "overwrite=False" not in call